Jac*_*ski 74 django django-rest-framework
我的模型看起来像这样:
class Category(models.Model):
parentCategory = models.ForeignKey('self', blank=True, null=True, related_name='subcategories')
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
Run Code Online (Sandbox Code Playgroud)
我设法用序列化器获得所有类别的平面json表示:
class CategorySerializer(serializers.HyperlinkedModelSerializer):
parentCategory = serializers.PrimaryKeyRelatedField()
subcategories = serializers.ManyRelatedField()
class Meta:
model = Category
fields = ('parentCategory', 'name', 'description', 'subcategories')
Run Code Online (Sandbox Code Playgroud)
现在我想要做的是子类别列表具有子类别的内联json表示而不是它们的ID.我怎么用django-rest-framework做到这一点?我试图在文档中找到它,但似乎不完整.
Tom*_*tie 62
而不是使用ManyRelatedField,使用嵌套的序列化程序作为您的字段:
class SubCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name', 'description')
class CategorySerializer(serializers.ModelSerializer):
parentCategory = serializers.PrimaryKeyRelatedField()
subcategories = serializers.SubCategorySerializer()
class Meta:
model = Category
fields = ('parentCategory', 'name', 'description', 'subcategories')
Run Code Online (Sandbox Code Playgroud)
如果您想处理任意嵌套字段,您应该查看自定义文档的默认字段部分.您当前无法直接将序列化程序声明为字段,但您可以使用这些方法覆盖默认情况下使用的字段.
class CategorySerializer(serializers.ModelSerializer):
parentCategory = serializers.PrimaryKeyRelatedField()
class Meta:
model = Category
fields = ('parentCategory', 'name', 'description', 'subcategories')
def get_related_field(self, model_field):
# Handles initializing the `subcategories` field
return CategorySerializer()
Run Code Online (Sandbox Code Playgroud)
实际上,正如你所指出的,上述情况并不完全正确.这有点像黑客,但您可能会尝试在已经声明序列化程序后添加字段.
class CategorySerializer(serializers.ModelSerializer):
parentCategory = serializers.PrimaryKeyRelatedField()
class Meta:
model = Category
fields = ('parentCategory', 'name', 'description', 'subcategories')
CategorySerializer.base_fields['subcategories'] = CategorySerializer()
Run Code Online (Sandbox Code Playgroud)
声明递归关系的机制是需要添加的.
编辑:请注意,现在有一个专门处理这种用例的第三方软件包.请参阅djangorestframework-recursive.
Mar*_*ian 44
@ wjin的解决方案对我很有用,直到我升级到Django REST框架3.0.0,它弃用了tonative.这是我的DRF 3.0解决方案,稍作修改.
假设您有一个带有自引用字段的模型,例如名为"replies"的属性中的线程注释.您有此注释线程的树表示,并且您希望序列化树
首先,定义可重用的RecursiveField类
class RecursiveField(serializers.Serializer):
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data
Run Code Online (Sandbox Code Playgroud)
然后,对于序列化程序,使用RecursiveField序列化"回复"的值
class CommentSerializer(serializers.Serializer):
replies = RecursiveField(many=True)
class Meta:
model = Comment
fields = ('replies, ....)
Run Code Online (Sandbox Code Playgroud)
简单易用,您只需要4行代码即可实现可重复使用的解决方案.
注意:如果你的数据结构比树更复杂,比如说有 向无环图(FANCY!)那么你可以试试@ wjin的包 - 看看他的解决方案.但是我对基于MPTTModel的树的解决方案没有任何问题.
wji*_*jin 24
在这里比赛的后期,但这是我的解决方案.假设我正在序列化一个Blah,还有多个Blah类型的孩子.
class RecursiveField(serializers.Serializer):
def to_native(self, value):
return self.parent.to_native(value)
Run Code Online (Sandbox Code Playgroud)
使用此字段,我可以序列化具有许多子对象的递归定义对象
class BlahSerializer(serializers.Serializer):
name = serializers.Field()
child_blahs = RecursiveField(many=True)
Run Code Online (Sandbox Code Playgroud)
我为DRF3.0编写了一个递归字段,并将其打包为pip https://pypi.python.org/pypi/djangorestframework-recursive/
ypr*_*rez 22
另一个适用于Django REST Framework 3.3.2的选项:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name', 'parentid', 'subcategories')
def get_fields(self):
fields = super(CategorySerializer, self).get_fields()
fields['subcategories'] = CategorySerializer(many=True)
return fields
Run Code Online (Sandbox Code Playgroud)
另一种选择是在序列化模型的视图中进行递归.这是一个例子:
class DepartmentSerializer(ModelSerializer):
class Meta:
model = models.Department
class DepartmentViewSet(ModelViewSet):
model = models.Department
serializer_class = DepartmentSerializer
def serialize_tree(self, queryset):
for obj in queryset:
data = self.get_serializer(obj).data
data['children'] = self.serialize_tree(obj.children.all())
yield data
def list(self, request):
queryset = self.get_queryset().filter(level=0)
data = self.serialize_tree(queryset)
return Response(data)
def retrieve(self, request, pk=None):
self.object = self.get_object()
data = self.serialize_tree([self.object])
return Response(data)
Run Code Online (Sandbox Code Playgroud)
我能够用a实现这个结果serializers.SerializerMethodField.我不确定这是不是最好的方式,但对我有用:
class CategorySerializer(serializers.ModelSerializer):
subcategories = serializers.SerializerMethodField(
read_only=True, method_name="get_child_categories")
class Meta:
model = Category
fields = [
'name',
'category_id',
'subcategories',
]
def get_child_categories(self, obj):
""" self referral field """
serializer = CategorySerializer(
instance=obj.subcategories_set.all(),
many=True
)
return serializer.data
Run Code Online (Sandbox Code Playgroud)
我最近遇到了同样的问题,并提出了迄今为止似乎有效的解决方案,即使是任意深度.解决方案是Tom Christie的一个小修改:
class CategorySerializer(serializers.ModelSerializer):
parentCategory = serializers.PrimaryKeyRelatedField()
def convert_object(self, obj):
#Add any self-referencing fields here (if not already done)
if not self.fields.has_key('subcategories'):
self.fields['subcategories'] = CategorySerializer()
return super(CategorySerializer,self).convert_object(obj)
class Meta:
model = Category
#do NOT include self-referencing fields here
#fields = ('parentCategory', 'name', 'description', 'subcategories')
fields = ('parentCategory', 'name', 'description')
#This is not needed
#CategorySerializer.base_fields['subcategories'] = CategorySerializer()
Run Code Online (Sandbox Code Playgroud)
我不确定它在任何情况下都能可靠地工作,不过......
该解决方案与此处发布的其他解决方案几乎相似,但在根级别的子重复问题方面略有不同(如果您认为这是一个问题)。举个例子
class RecursiveSerializer(serializers.Serializer):
def to_representation(self, value):
serializer = self.parent.parent.__class__(value, context=self.context)
return serializer.data
class CategoryListSerializer(ModelSerializer):
sub_category = RecursiveSerializer(many=True, read_only=True)
class Meta:
model = Category
fields = (
'name',
'slug',
'parent',
'sub_category'
)
Run Code Online (Sandbox Code Playgroud)
如果你有这个观点
class CategoryListAPIView(ListAPIView):
queryset = Category.objects.all()
serializer_class = CategoryListSerializer
Run Code Online (Sandbox Code Playgroud)
这将产生以下结果,
[
{
"name": "parent category",
"slug": "parent-category",
"parent": null,
"sub_category": [
{
"name": "child category",
"slug": "child-category",
"parent": 20,
"sub_category": []
}
]
},
{
"name": "child category",
"slug": "child-category",
"parent": 20,
"sub_category": []
}
]
Run Code Online (Sandbox Code Playgroud)
这里parent category有一个child categoryjson 表示正是我们想要它表示的。
child category但您可以看到在根级别有重复。
正如有些人在上面发布的答案的评论部分中询问的那样,我们如何才能在根级别停止这种子重复,只需使用 过滤您的查询集parent=None,如下所示
class CategoryListAPIView(ListAPIView):
queryset = Category.objects.filter(parent=None)
serializer_class = CategoryListSerializer
Run Code Online (Sandbox Code Playgroud)
它会解决问题。
注意:这个答案可能与问题没有直接关系,但问题在某种程度上是相关的。而且这种使用方法RecursiveSerializer是昂贵的。如果您使用其他有性能倾向的选项会更好。
小智 6
这是适用于drf 3.0.5和django 2.7.4的caipirginka解决方案的改编:
class CategorySerializer(serializers.ModelSerializer):
def to_representation(self, obj):
#Add any self-referencing fields here (if not already done)
if 'branches' not in self.fields:
self.fields['subcategories'] = CategorySerializer(obj, many=True)
return super(CategorySerializer, self).to_representation(obj)
class Meta:
model = Category
fields = ('id', 'description', 'parentCategory')
Run Code Online (Sandbox Code Playgroud)
请注意,使用object和many = True属性调用第6行中的CategorySerializer.
我以为我会参加这个有趣的活动!
通过wjin和Mark Chackerian,我创建了一个更通用的解决方案,它适用于具有直通模型的直接树状模型和树结构.我不确定这是否属于它自己的答案,但我想我不妨把它放在某个地方.我包含了一个max_depth选项,它可以防止无限递归,在最深层次上,子节点被表示为URLS(如果你不想它是一个url,那就是最后的else子句).
from rest_framework.reverse import reverse
from rest_framework import serializers
class RecursiveField(serializers.Serializer):
"""
Can be used as a field within another serializer,
to produce nested-recursive relationships. Works with
through models, and limited and/or arbitrarily deep trees.
"""
def __init__(self, **kwargs):
self._recurse_through = kwargs.pop('through_serializer', None)
self._recurse_max = kwargs.pop('max_depth', None)
self._recurse_view = kwargs.pop('reverse_name', None)
self._recurse_attr = kwargs.pop('reverse_attr', None)
self._recurse_many = kwargs.pop('many', False)
super(RecursiveField, self).__init__(**kwargs)
def to_representation(self, value):
parent = self.parent
if isinstance(parent, serializers.ListSerializer):
parent = parent.parent
lvl = getattr(parent, '_recurse_lvl', 1)
max_lvl = self._recurse_max or getattr(parent, '_recurse_max', None)
# Defined within RecursiveField(through_serializer=A)
serializer_class = self._recurse_through
is_through = has_through = True
# Informed by previous serializer (for through m2m)
if not serializer_class:
is_through = False
serializer_class = getattr(parent, '_recurse_next', None)
# Introspected for cases without through models.
if not serializer_class:
has_through = False
serializer_class = parent.__class__
if is_through or not max_lvl or lvl <= max_lvl:
serializer = serializer_class(
value, many=self._recurse_many, context=self.context)
# Propagate hereditary attributes.
serializer._recurse_lvl = lvl + is_through or not has_through
serializer._recurse_max = max_lvl
if is_through:
# Delay using parent serializer till next lvl.
serializer._recurse_next = parent.__class__
return serializer.data
else:
view = self._recurse_view or self.context['request'].resolver_match.url_name
attr = self._recurse_attr or 'id'
return reverse(view, args=[getattr(value, attr)],
request=self.context['request'])
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
36917 次 |
| 最近记录: |