Как сериализовать модели Django с вложенными объектами (Django REST Framework)

Если у меня есть два сериализатора, один из которых вложен, как настроить метод restore_object? Например, если у меня определены следующие сериализаторы, как определить поле объекта восстановления для моего вложенного сериализатора? Из документации не очевидно, как поступать в таком случае.

class UserSerializer(serializers.Serializer):
    first_name = serializers.CharField(required=True, max_length=30)
    last_name = serializers.CharField(required=True, max_length=30)
    username = serializers.CharField(required=True, max_length=30)
    email = serializers.EmailField(required=True)
    password = serializers.CharField(required=True)

    def restore_object(self, attrs, instance=None):
        if instance:
            instance.first_name = attrs.get('first_name', instance.first_name)
            instance.last_name = attrs.get('last_name', instance.last_name)
            instance.email = attrs.get('email', instance.email)
            instance.password = attrs.get('password', instance.password)

class UserProfileSerializer(serializers.Serializer):
    user = UserSerializer()
    bio = serializers.CharField()
    def restore_object(self, attrs, instance=None):
        if instance:
            instance.bio = attrs.get('bio', instance.bio)
            instance.user = ?????

person user1876508    schedule 10.05.2013    source источник


Ответы (1)


Важное примечание. Похоже, вы используете старую методологию "Пользователь/Профиль". Начиная с Django 1.5 нет разделения моделей пользователя по умолчанию и ваших пользовательских моделей профиля. Вы должны создать свою собственную пользовательскую модель и использовать ее вместо модели по умолчанию: Пользовательский профиль пользователя @ Django Docs

Сериализация

Я хочу представить вам другой подход к сериализации и восстановлению моделей. Все объекты модели можно сериализовать с помощью следующего фрагмента кода:

from django.core import serializers
serialized_data = serializers.serialize("json", myInstance)

или сериализовать более одного объекта:

serialized_data = serializers.serialize("json", User.objects.all())

Затем внешние ключи и отношения m2m сохраняются в массиве идентификаторов.

Если вы хотите сериализовать только подмножество полей:

serialized_data = serializers.serialize("json", myUserInstance, fields=('first_name ','last_name ','email ','password '))

Чтобы сохранить профиль пользователя, вы должны просто написать:

serialized_data = serializers.serialize("json", myUserProfileInstance)

Идентификатор пользователя сохраняется в сериализованных данных и выглядит следующим образом:

{
    "pk": 1,
    "model": "profile.UserProfile",
    "fields": {
        "bio": "self-taught couch potato",
        "user": 1
    }
}

Если вы хотите также использовать связанные пользовательские поля в сериализации, вам необходимо изменить модель пользователя:

class UserManager(models.Manager):
    def get_by_natural_key(self, first_name, last_name):
        return self.get(first_name=first_name, last_name=last_name)

class User(models.Model):
    objects = UserManager()

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    ...

    def natural_key(self):
        return (self.first_name, self.last_name)

    class Meta:
        unique_together = (('first_name', 'last_name'),)

При сериализации с естественными ключами вам нужно добавить аргумент use_natural_keys:

serialized_data = serializers.serialize("json", myUserProfileInstance, use_natural_keys=True)

Что приводит к следующему выводу:

{
    "pk": 2,
    "model": "profile.UserProfile",
    "fields": {
        "bio": "time-traveling smartass",
        "user": ["Dr.", "Who"]
    }
}

Десериализация

Десериализовать и сохранить так же просто, как:

for deserialized_object in serializers.deserialize("json", serialized_data):
    deserialized_object.save()

Дополнительную информацию можно найти в документации по Django: Сериализация объектов Django

person Alp    schedule 28.05.2013
comment
Это интересный подход, и я согласен с вами, что мне следует использовать новую модель. Но как мне решить, какие поля выводить. Я уверен, что люди, использующие API, не захотят видеть модель: profile.UserProfile - person user1876508; 28.05.2013
comment
Или это должно быть поздравлено через модуль json? - person user1876508; 28.05.2013
comment
Вы можете просто использовать serialized_json = simplejson.loads(serialized_data) и предоставить только часть полей JSON: serialized_json[0]['fields'] - person Alp; 28.05.2013
comment
Кстати, если у вас есть вопросы, не стесняйтесь спрашивать, и я был бы рад углубиться в эту тему. и если мой ответ помог, пожалуйста, подумайте о том, чтобы проголосовать или принять его. - person Alp; 03.06.2013
comment
Это отличная находка. Одно исправление, однако, вторым аргументом для serializers.serialize() должен быть итератор, поэтому передача экземпляра приведет к ошибке TypeError. Оберните экземпляр в список/кортеж, и все готово. Протестировано в Джанго 1.6.2 - person igniteflow; 18.03.2014
comment
Возможно, это изменилось с тех пор, как этот ответ был впервые написан, но попытка сериализовать отдельные экземпляры таким образом генерирует ошибки Not iterable. Чтобы исправить, используйте, например. serialized_data = serializers.serialize("json", [myUserProfileInstance, ]) - person shacker; 10.01.2017