Создание пользовательского сериализатора в django

Я столкнулся с проблемой, потому что мне нужно создать пользовательский сериализатор, чтобы удовлетворить свои потребности.

Вот мои модели:

class PublicID(models.Model):
    TYPE_FIELDS = (
        ('M', 'monkey'),
        ('A', 'alien'),
        ('P', 'person'),
    )

    public_id = models.CharField(unique=True, max_length=48, editable=False)
    obj_type = models.CharField(choices=TYPE_FIELDS, max_length=1)


class Request(models.Model):
    source = models.ForeignKey(PublicID, related_name='source_request_set',
        on_delete=models.CASCADE)
    dest = models.ForeignKey(PublicID, related_name='dest_request_set',
        on_delete=models.CASCADE)


class Monkey(models.Model):
    nickname = models.CharField(max_length=255)
    public_id = models.OneToOneField(PublicID, related_name='monkey',
        blank=True, on_delete=models.CASCADE)


class Alien(models.Model):
    alias = models.CharField(max_length=255)
    public_id = models.OneToOneField(PublicID, related_name='alien',
        blank=True, on_delete=models.CASCADE)


class Person(models.Model):
    first_name = models.CharField(max_length=255)
    public_id = models.OneToOneField(PublicID, related_name='person',
        blank=True, on_delete=models.CASCADE)

Обратите внимание, что все типы obj_type могут посылать Request каждому типу obj_type. Например, у нас может быть Monkey, который посылает запрос Alien через их уникальные PublicID

Мой сериализатор выглядит следующим образом:

class RequestSerializer(serializers.ModelSerializer):
    class Meta:
        model = Request
        exclude = ['id']
    
    def validate_source(self, value):
        obj_type = value.get('obj_type')
        if obj_type is None:
            raise serializers.ValidationError('`obj_type` field is required.')
        # other checks
        return value

    def validate_dest(self, value):
        obj_type = value.get('obj_type')
        if obj_type is None:
            raise serializers.ValidationError('`obj_type` field is required.')
        # other checks
        return value
    
    def run_validation(self, attrs):
        source = attrs.get('source')
        dest = attrs.get('dest')
        if source is None or dest is None:
            raise serializers.ValidationError('`source` and `dest` fields are '
                                              'required.')
        self.validate_source(source)
        self.validate_dest(dest)
        return attrs
    
    def to_representation(self, instance):
        source_type = instance.source.obj_type
        dest_type = instance.dest.obj_type
        print(source_type, dest_type)
        representation = {}
        if source_type == 'monkey':
            representation['source'] = {
                'nickname': instance.source.monkey.nickname,
                'obj_type': 'monkey',
            }
        elif source_type == 'alien':
            representation['source'] = {
                'alias': instance.source.alien.alias,
                'obj_type': 'alien',
            }
        elif source_type == 'person':
            representation['source'] = {
                'username': instance.source.person.username,
                'obj_type': 'person',
            }
        
        if dest_type == 'monkey':
            representation['dest'] = {
                'nickname': instance.dest.monkey.nickname,
                'obj_type': 'monkey',
            }
        elif dest_type == 'alien':
            representation['dest'] = {
                'alias': instance.dest.alien.alias,
                'obj_type': 'alien',
            }
        elif dest_type == 'person':
            representation['dest'] = {
                'username': instance.dest.person.username,
                'obj_type': 'person',
            }

        return representation

Ожидаемое поведение:

Мой сериализатор должен быть способен:

  1. Получив Request, он должен уметь сериализовать его в:
{
    'source': {'nickname': 'john', 'obj_type': 'monkey'},
    'dest': {'alias': 'jim', 'obj_type': 'alien'}
}

Это успешно завершено благодаря модификации метода to_representation.

  1. Учитывая сериализованные данные (предположим, что это данные, полученные выше в пункте (1)), он должен быть в состоянии проверить их, чтобы использовать их в методах create или update. Поскольку я переопределил run_validation и создал методы validate_<field>, он успешно валидирует данные.

Мой вопрос в том, где правильное место для десериализации данных:

{
    'source': {'nickname': 'john', 'obj_type': 'monkey'},
    'dest': {'alias': 'jim', 'obj_type': 'alien'}
}

в объект Request, который имеет source и dest, которые являются объектами PublicID? В ходе моих исследований я нашел лучший способ - переопределить метод to_internal_value(), чтобы получить объект, если он существует, или вызвать исключение, если его нет. Если он не существует, я буду ловить исключение и использовать .save(create=True) вместо него.

  1. Является ли это наиболее питоническим способом и лучшей практикой django для этого?

Другое дело, что если я проверю данные и сохраню возвращаемое значение сериализатора в переменной s, я не смогу получить доступ к s.data.

in to_representation

source_type = instance.source.obj_type

AttributeError: объект 'ReturnDict' не имеет атрибута 'source'

Однако, я могу получить доступ к s.validated_data и значения правильные.

  1. почему вызывается метод to_representation(), когда я пытаюсь получить доступ к s.data? Я предполагал, что поскольку экземпляр сериализатора был создан из сериализованных данных, процесс сериализации не будет происходить снова.

  2. Является ли проблемой то, что я не могу получить доступ к s.data? Моя реализация неверна?

Вернуться на верх