Django的REST框架嵌套的自我指涉的对象(Django rest framework nest

2019-06-21 03:39发布

我的模型,看起来像这样:

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)

我设法与串行所有类别的平板JSON表示:

class CategorySerializer(serializers.HyperlinkedModelSerializer):
    parentCategory = serializers.PrimaryKeyRelatedField()
    subcategories = serializers.ManyRelatedField()

    class Meta:
        model = Category
        fields = ('parentCategory', 'name', 'description', 'subcategories')

现在我要做的就是为子类别列表,以便在子类别,而不是他们的ID的在线JSON表示。 我会怎么做,与Django的REST的框架? 我试图找到它的文档,但似乎不完整的。

Answer 1:

除了使用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')

如果你想处理任意嵌套的字段,你应该看一看的自定义默认领域的文档的一部分。 你不能直接当前在声明本身就是一个串行的领域,但你可以使用这些方法来覆盖系统默认使用的是什么领域。

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()

事实上,因为你已经注意到上述是不完全正确。 这是一个黑客位的,但你可以尝试在加入后的串行已经声明的字段。

class CategorySerializer(serializers.ModelSerializer):
    parentCategory = serializers.PrimaryKeyRelatedField()

    class Meta:
        model = Category
        fields = ('parentCategory', 'name', 'description', 'subcategories')

CategorySerializer.base_fields['subcategories'] = CategorySerializer()

声明递归关系的机制是什么,需要添加。


编辑 :请注意,现在有可用的第三方包,用这种用例专门涉及。 见djangorestframework递归 。



Answer 2:

@ wjin的解决方案是伟大的工作,我,直到我升级到Django的REST框架3.0.0,它不赞成to_native。 这里是我的DRF 3.0解决方案,这是一个轻微的修改。

假设你有一个自我指涉的领域模型,例如线程在一个名为“回复”属性意见。 你有这样的评论跟帖的树表示,并要序列树

首先,定义您的可重复使用的RecursiveField类

class RecursiveField(serializers.Serializer):
    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)
        return serializer.data

然后,你的串行器,使用的RecursiveField连载“回复”的价值

class CommentSerializer(serializers.Serializer):
    replies = RecursiveField(many=True)

    class Meta:
        model = Comment
        fields = ('replies, ....)

易peasy,而你只需要4行代码的可重用的解决方案。

注意:如果你的数据结构更复杂一些比一棵树,就像说一个向无环图 (FANCY!),那么你可以尝试@ wjin的包-看到他的解决方案。 但是我还没有与该解决方案基于MPTTModel树的任何问题。



Answer 3:

晚在这里比赛,但这里是我的解决方案。 比方说,我是序列化胡说,有多个孩子也是类型胡说。

    class RecursiveField(serializers.Serializer):
        def to_native(self, value):
            return self.parent.to_native(value)

利用这个字段,我可以序列化有很多的孩子,我的对象递归定义的对象

    class BlahSerializer(serializers.Serializer):
        name = serializers.Field()
        child_blahs = RecursiveField(many=True)

我写了DRF3.0递归领域和包装它PIP https://pypi.python.org/pypi/djangorestframework-recursive/



Answer 4:

与Django的REST框架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


Answer 5:

另一种选择是在序列化模型视图递归。 下面是一个例子:

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)


Answer 6:

最近,我有同样的问题,并似乎工作至今,即使是任意深度的解决方案上来。 该解决方案是从汤姆克里斯蒂所述一个的小修改:

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()

我不知道它可以在任何情况下可靠地工作,但...



Answer 7:

我可以使用来实现这一结果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


Answer 8:

这是从caipirginka的解决方案,在DRF 3.0.5和2.7.4的Django的作品改编:

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')

请注意,在6号线的CategorySerializer被调用的对象和许多= true属性。



Answer 9:

我想我会参加的乐趣!

通过wjin和马克Chackerian我创建了一个更通用的解决方案,它适用于直接树状模型和树形结构,其通过模型有一个。 我不知道,如果这属于它自己的答案,但我想我还不如把它的地方。 我包括一个MAX_DEPTH选项,这将阻止无限递归,在最深层次的孩子被表示为URL(这是最后的else子句,如果你宁愿它不是一个URL)。

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'])


Answer 10:

随着Django的REST框架3.3.1,我需要下面的代码被添加到种类的子类:

models.py

class Category(models.Model):

    id = models.AutoField(
        primary_key=True
    )

    name = models.CharField(
        max_length=45, 
        blank=False, 
        null=False
    )

    parentid = models.ForeignKey(
        'self',
        related_name='subcategories',
        blank=True,
        null=True
    )

    class Meta:
        db_table = 'Categories'

serializers.py

class SubcategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = Category
        fields = ('id', 'name', 'parentid')


class CategorySerializer(serializers.ModelSerializer):
    subcategories = SubcategorySerializer(many=True, read_only=True)

    class Meta:
        model = Category
        fields = ('id', 'name', 'parentid', 'subcategories')


文章来源: Django rest framework nested self-referential objects