[django] Django 나머지 프레임 워크 중첩 된 자체 참조 개체

다음과 같은 모델이 있습니다.

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)

serializer를 사용하여 모든 범주의 평면 json 표현을 얻을 수있었습니다.

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

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

이제 내가하고 싶은 것은 하위 카테고리 목록이 ID 대신 하위 카테고리의 인라인 json 표현을 갖는 것입니다. django-rest-framework로 어떻게할까요? 문서에서 찾으려고했지만 불완전한 것 같습니다.



답변

ManyRelatedField를 사용하는 대신 중첩 된 serializer를 필드로 사용합니다.

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

사실, 위의 내용은 옳지 않습니다. 이것은 약간의 해킹이지만 serializer가 이미 선언 된 후에 필드를 추가 할 수 있습니다.

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

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

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

재귀 적 관계를 선언하는 메커니즘을 추가해야합니다.


편집 : 이제 이러한 종류의 사용 사례를 특별히 다루는 타사 패키지를 사용할 수 있습니다. djangorestframework-recursive를 참조하십시오 .


답변

@wjin의 솔루션은 to_native 를 사용하지 않는 Django REST 프레임 워크 3.0.0으로 업그레이드 할 때까지 잘 작동 했습니다. . 다음은 약간 수정 된 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

그런 다음 serializer의 경우 RecursiveField를 사용하여 “replies”값을 직렬화합니다.

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

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

쉽고 간단하며 재사용 가능한 솔루션을 위해 4 줄의 코드 만 필요합니다.

참고 : 방향성 비순환 그래프 (FANCY!) 와 같이 데이터 구조가 트리보다 더 복잡한 경우 @wjin의 패키지를 사용해 볼 수 있습니다. 그의 솔루션을 참조하십시오. 하지만 MPTTModel 기반 트리에 대한이 솔루션에는 문제가 없었습니다.


답변

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


답변

여기 게임이 늦었지만 여기에 내 해결책이 있습니다. Blah 유형의 자식이 여러 개있는 Blah를 직렬화한다고 가정 해 보겠습니다.

    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/에 대해 패키징했습니다.


답변

나는이 결과를 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


답변

또 다른 옵션은 모델을 직렬화하는 뷰에서 재귀하는 것입니다. 예를 들면 다음과 같습니다.

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)


답변

나는 최근에 같은 문제를 겪었고 지금까지 임의의 깊이에서도 작동하는 것처럼 보이는 해결책을 찾았습니다. 해결책은 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()

하지만 어떤 상황 에서도 안정적으로 작동 할 수 있을지 모르겠지만 …