Django Rest Framework сериализация многих порождает нетиповые экземпляры сериализованного отношения
Я довольно новичок в Django Rest Framework, и я пытался написать сериализатор для одной из моих моделей. Для моего проекта Я намерен вывести результат json в соответствии со стандартами API JSON, и для этого я использую SerializerMethodField, в котором я вызываю метод get_data() следующим образом:
Models.py
class Level(MPTTModel):
name = models.CharField(max_length=100)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
class MPTTMeta:
order_insertion_by = ['name']
class Group(models.Model):
name = models.CharField(max_length=100)
level_set = models.ManyToManyField(Level, blank=True)
Serializers.py
class LevelSerializer(serializers.ModelSerializer):
data = serializers.SerializerMethodField()
class Meta:
model = Level
fields = ('data',)
def get_data(self, instance):
return {
"type": "Level",
"uuid": instance.id_key(),
"attributes": {
"name": instance.name,
},
"relationships": {
"children": self.get_children_recursive(),
},
}
def get_children_recursive(self, child=None):
"""
Generates a tree of levels structured according to JSON API standards.
"""
if not child:
children = self.instance.get_children()
level = self.instance
else:
children = child.get_children()
level = child
tree = {
'data': {
'type': 'Level',
'uuid': level.id_key(),
'attributes': {
'name': level.name,
},
'relationships': {
'children': [],
'parents': [],
}
}
}
for child in children:
tree['data']['relationships']['children'].append(self.get_children_recursive(child))
return tree
class GroupSerializer(serializers.ModelSerializer):
root_level_set = LevelSerializer(many=True)
class Meta:
model = Group
fields = ('id_key', 'name', 'root_level_set')
Странно то, что если я иду в оболочку и пытаюсь сериализовать экземпляр уровня, он работает нормально, но пытается сериализация группы дает мне ошибку в get_children_recursive() в строке с if not child statement, говорящей, что 'NoneType' object has no attribute 'get_children'. Выходные данные таковы:
Запуск:
from core.serializers import LevelSerializer
from core.models import Level
lvl = Level.objects.all()[0]
serializer = LevelSerializer(lvl)
print(serializer.data)
Выводит вложенный уровень и его подуровни в соответствии со структурой JSON, разработанной мной в соответствии со стандартами API JSON.
Хотя если я побегу:
from core.serializers import GroupSerializer
from core.models import Group
grp = Group.objects.all()[0]
serializer = GroupSerializer(grp)
print(serializer.data)
Выходы:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 503, in data
ret = super(Serializer, self).data
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 239, in data
self._data = self.to_representation(self.instance)
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 472, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 614, in to_representation
self.child.to_representation(item) for item in iterable
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 614, in <listcomp>
self.child.to_representation(item) for item in iterable
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/serializers.py", line 472, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File "/home/maumau/.virtualenvs/olist/lib/python3.5/site-packages/rest_framework/fields.py", line 1653, in to_representation
return method(value)
File "/mnt/SHARED-DRIVE/Workspace/interview-tests/work-at-olist/core/serializers.py", line 20, in get_data
"children": self.get_children_recursive(),
File "/mnt/SHARED-DRIVE/Workspace/interview-tests/work-at-olist/core/serializers.py", line 29, in get_children_recursive
children = self.instance.get_children()
AttributeError: 'NoneType' object has no attribute 'get_children'
Мне кажется, не имеет смысла, что сериализация уровня IT self обладает набором атрибутов экземпляра, в то время как сериализация группы, которая вызывает сериализацию уровня также, не делает. Есть зацепка?
2 ответов:
Зачем создавать все самому? пусть djrf немного поколдует:)
class LevelSerializer(serializers.ModelSerializer): type = serializers.SerializerMethodField() attributes = serializers.SerializerMethodField() relationships = serializers.SerializerMethodField() def get_type(self, instance): return "Level" def get_attributes(self, instance): return { "name": instance.name } def get_relationships(self, instance): return { "children": self.__class__(instance.get_children(), many=True).data, "parents": [] } class Meta: model = Level fields = ('type', 'attributes', 'relationships') class GroupSerializer(serializers.ModelSerializer): level_set = LevelSerializer(many=True) class Meta: model = Group fields = ('name', 'level_set')Почему ваш код не работает? из-за неправильного кода:
- посмотрите, чем отличается вызов
LevelSerializerв тех 2 случаях - один параметр -many=True; Когдаmany=True- сериализатор не имеетinstanceсвойства- рядом с (1), первый запуск
get_children_recursiveбудет дублировать первый элемент как первый дочерний- используя ваш подход, вам вообще не нужно
ModelSerializer, потому что все делается вручную - в таком случае, простойSerializerбудет лучше- создавая виртуальное поле
data- вы усложняете свой код, почему бы не использовать данные напрямую, чтобы избежать словарей с одним ключомdataМой пример кода отсутствует
id_keyполе - вы не задокументировали его здесь, но добавление его должно быть brezeИспользуя дополнительный модуль
rest_framework_recursive, код можно упростить:from rest_framework_recursive.fields import RecursiveField class LevelSerializer(serializers.ModelSerializer): type = serializers.SerializerMethodField() children = serializers.ListField(child=RecursiveField(), source='get_children') def get_type(self, instance): return "Level" class Meta: model = Level fields = ('type', 'name', 'children')
Имя атрибута в сериальзиере:
root_level_set. В модели:level_set.DRF пытается найти атрибут
root_level_setв группе, не находит его и заменяет его наNone. Вот почему вы получаете такую ошибку.Исправить:
- переименовать
root_level_set->level_setв сериализаторе.- или добавить
sourceв поле:root_level_set = LevelSerializer(source='level_set', many=True)
Comments