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 обладает набором атрибутов экземпляра, в то время как сериализация группы, которая вызывает сериализацию уровня также, не делает. Есть зацепка?

250   2  

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

Почему ваш код не работает? из-за неправильного кода:

  1. посмотрите, чем отличается вызов LevelSerializer в тех 2 случаях - один параметр - many=True; Когда many=True - сериализатор не имеет instance свойства
  2. рядом с (1), первый запуск get_children_recursive будет дублировать первый элемент как первый дочерний
  3. используя ваш подход, вам вообще не нужно ModelSerializer, потому что все делается вручную - в таком случае, простой Serializer будет лучше
  4. создавая виртуальное поле 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. Вот почему вы получаете такую ошибку.

Исправить:

  1. переименовать root_level_set -> level_set в сериализаторе.
  2. или добавить source в поле: root_level_set = LevelSerializer(source='level_set', many=True)
    Ничего не найдено.

Добавить ответ:
Отменить.