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 기반 트리에 대한이 솔루션에는 문제가 없었습니다.
여기 게임이 늦었지만 여기 내 해결책이 있습니다. 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/에 대해 패키징했습니다.
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
또 다른 옵션은 모델을 직렬화하는 뷰에서 재귀하는 것입니다. 예를 들면 다음과 같습니다.
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()
하지만 어떤 상황 에서도 안정적으로 작동 할 수 있을지 모르겠지만 ...
나는이 결과를 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
이것은 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')
6 번째 줄의 CategorySerializer는 개체 및 many = True 속성과 함께 호출됩니다.
나는 내가 재미에 동참 할 것이라고 생각했다!
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'])
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')
참고URL : https://stackoverflow.com/questions/13376894/django-rest-framework-nested-self-referential-objects
'code' 카테고리의 다른 글
문자열에서 HTML 태그를 제거하는 정규식 (0) | 2020.10.31 |
---|---|
Android에서 프로그래밍 방식으로 ScreenSize를 얻는 방법 (0) | 2020.10.31 |
Google Play 서비스를 버전 13으로 업데이트 한 후 오류가 발생했습니다. (0) | 2020.10.31 |
사용자 정의 UITableViewCell에서 자동 레이아웃이 무시됩니다. (0) | 2020.10.31 |
PHP : 숫자 인덱스에서 연관 배열 키를 얻는 방법? (0) | 2020.10.31 |