Руководство по API платформы Django REST (9): Сериализация

задняя часть Python API Django

Руководство по API платформы Django REST (1): запросы
Руководство по API платформы Django REST (2): ответы
Руководство по API платформы Django REST (3): представления
Руководство по API платформы Django REST (4): общие представления
Руководство по API платформы Django REST (5): наборы представлений
Руководство по API Django REST framework (6): Маршрутизация
Руководство по API платформы Django REST (7): анализ
Руководство по API Django REST framework (8): рендеринг
Руководство по API платформы Django REST (9): Сериализация

Официальная оригинальная ссылка

Serializers

Сериализаторы позволяют преобразовывать сложные данные, такие как наборы запросов и экземпляры моделей, в собственные типы данных Python, которые затем можно легко представить какJSON,XMLили другие типы контента. Сериализатор также обеспечивает десериализацию, которая может преобразовать проанализированные данные обратно в сложный тип после предварительной проверки входящих данных.

Классы сериализации в среде REST и DjangoFormиModelFormКлассы очень похожи. мы предоставляемSerializerкласс, который предоставляет мощный общий способ управления выводом ответа, иModelSerializerкласс, предоставляющий эффективные ярлыки для создания сериализаций, обрабатывающих экземпляры моделей и наборы запросов.

Объявить класс сериализации

Сначала создайте простой объект для примера:

from datetime import datetime

class Comment(object):
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()

comment = Comment(email='leila@example.com', content='foo bar')

Объявите класс сериализации, используйте его для сериализации и десериализации с помощьюCommentДанные, соответствующие объекту.

Объявление класса сериализации очень похоже на объявление формы:

from rest_framework import serializers

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

сериализованный объект

теперь доступноCommentSerializerсериализовать комментарий или список комментариев. Аналогичным образом используйтеSerializerКласс очень похож на использованиеFormсвоего рода.

serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}

На данный момент экземпляр модели был преобразован в собственный тип данных Python. Чтобы завершить процесс сериализации, отобразите данные какjson.

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'

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

Десериализация аналогична. Сначала мы анализируем поток в собственный тип данных Python...

from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser

stream = BytesIO(json)
data = JSONParser().parse(stream)

... затем мы восстанавливаем эти собственные типы данных в проверенный словарь данных.

serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

сохранить экземпляр

Если вы хотите иметь возможность возвращать полный экземпляр объекта на основе проверенных данных, вам необходимо реализовать.create()и.update()один или оба метода. Например:

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        return Comment(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        return instance

Если экземпляр объекта соответствует модели Django, вам также необходимо убедиться, что эти методы сохраняют объект в базе данных. еслиCommentявляется моделью Django, методы могут выглядеть так:

    def create(self, validated_data):
        return Comment.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.content = validated_data.get('content', instance.content)
        instance.created = validated_data.get('created', instance.created)
        instance.save()
        return instance

Теперь при десериализации данных мы можем вызвать.save()Возвращает экземпляр объекта на основе проверенных данных.

comment = serializer.save()

перечислить.save()Будет создан новый экземпляр или обновлен существующий экземпляр, в зависимости от того, был ли передан существующий экземпляр при создании экземпляра сериализованного класса:

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

.create()и.update()методы необязательны. Вы можете не реализовывать ни то, ни другое, или одно, или оба, в зависимости от варианта использования вашего класса сериализации.

Передать дополнительные свойства в.save()

Иногда вам может понадобиться, чтобы ваш код представления мог вводить дополнительные данные при сохранении экземпляра. Эти дополнительные данные могут содержать текущего пользователя, текущее время или любую другую информацию, которая не является частью запрошенных данных.

serializer.save(owner=request.user)

перечислить.create()или.update(), любые другие аргументы ключевого слова будут включены вvalidated_dataв параметре.

прямое покрытие.save().

При определенных обстоятельствах,.create()и.update()Названия методов могут не иметь смысла. Например, в «контактной форме» мы можем не создавать новый экземпляр, а отправить электронное письмо или другое сообщение.

В этих случаях есть возможность напрямую переопределить.save(), так как это более читабельно и осмысленно.

Возьмите каштан:

class ContactForm(serializers.Serializer):
    email = serializers.EmailField()
    message = serializers.CharField()

    def save(self):
        email = self.validated_data['email']
        message = self.validated_data['message']
        send_email(from=email, message=message)

Обратите внимание, что в приведенном выше случае прямой доступ должен бытьserializer .validated_dataАтрибуты.

проверять

При десериализации данных вам всегда нужно звонить, прежде чем пытаться получить доступ к данным проверки.is_valid()или сохраните экземпляр объекта. Если возникают какие-либо ошибки проверки, то.errorsСвойство будет содержать словарь, представляющий сообщение об ошибке. Например:

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}

Каждый ключ в словаре — это имя поля, а значение — это сообщение об ошибке (список строк), соответствующее этому полю.non_field_errorsКлюч также может присутствовать, и будут перечислены все общие ошибки проверки. можно использоватьNON_FIELD_ERRORS_KEY(устанавливается в файле настроек) для настройкиnon_field_errorsНазвание ключевого слова.

При десериализации списка элементов ошибки возвращаются в виде списка словарей, представляющих каждый десериализованный элемент.

Исключение возникло во время проверки данных

.is_valid()метод с необязательнымraise_exceptionФлаг, который заставит его бросить, если есть ошибки проверкиserializers.ValidationErrorаномальный.

Эти исключения автоматически обрабатываются обработчиком исключений по умолчанию, предоставляемым инфраструктурой REST, и возвращаются по умолчанию.HTTP 400 Bad Request.

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

Проверка на уровне поля

ты можешь пройтиSerializerдобавить подкласс.validate_<field_name>метод для указания пользовательской проверки на уровне поля. Они такие же, как в форме Django..clean_<field_name>Метод аналогичен.

Эти методы имеют только один параметр — значение поля, которое необходимо проверить.

Вашvalidate_<field_name>Метод должен возвращать значение проверки или бросатьserializers.ValidationError.

Например:

from rest_framework import serializers

class BlogPostSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    content = serializers.CharField()

    def validate_title(self, value):
        """
        Check that the blog post is about Django.
        """
        if 'django' not in value.lower():
            raise serializers.ValidationError("Blog post is not about Django")
        return value

Примечание: если ваш сериализатор объявляет<field_name>Параметрыrequired = False, то этот шаг проверки не будет выполнен, если поле не включено.

Проверка на уровне объекта

Если вы хотите выполнить дополнительную проверку нескольких полей, добавьте поле с именем.validate()способ добавить в свойSerializerв подклассе. Этот метод имеет только один параметр, который является значением поля (field-value) Словарь. При необходимости следует поднятьValidationErrorили просто верните проверенное значение. Например:

from rest_framework import serializers

class EventSerializer(serializers.Serializer):
    description = serializers.CharField(max_length=100)
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Check that the start is before the stop.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

валидатор

Отдельные поля в сериализаторе могут содержать валидаторы, объявляя их в экземпляре поля, например:

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])
    ...

Классы сериализации также могут содержать повторно используемые средства проверки, которые применяются ко всему набору данных полей. Эти валидаторы являются внутреннимиMetaОни включаются путем объявления их в классе следующим образом:

class EventSerializer(serializers.Serializer):
    name = serializers.CharField()
    room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
    date = serializers.DateField()

    class Meta:
        # Each room only has one event per day.
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

Не беда, если вы этого не понимаете, подробнее о верификации поговорим позже.

Доступ к исходным данным и экземплярам

При передаче начального объекта или набора запросов в сериализованный экземпляр класса объект будет использоваться как.instanceпоставка. Если начальный объект не передан, то.instanceимущество будетNone.

При передаче данных в сериализованный экземпляр класса неизмененные данные будут.initial_dataпоставка. Если аргумент ключевого слова данных не передается, то.initial_dataсобственности не будет.

Частичное обновление

По умолчанию сериализатор должен передавать значения для всех обязательных полей, иначе будет выброшена ошибка проверки. ты можешь использовать этоpartialпараметр, разрешающий частичные обновления.

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

Работа с вложенными объектами

Предыдущие примеры подходят для работы с объектами только с простыми типами данных, но иногда вам также необходимо иметь возможность представлять более сложные объекты, где некоторые свойства объекта могут не быть простыми типами данных, такими как строки, даты или целые числа. .

SerializerСам класс представляет собойField, который можно использовать для представления отношения, в котором один тип объекта вложен в другой тип объекта.

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Если вложенные объекты могут бытьNoneзначение,required = FalseФлаги для передачи во вложенные классы сериализации.

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # May be an anonymous user.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Аналогично, если вложенный объект является списком, он должен бытьmany = TrueФлаги для передачи во вложенные классы сериализации.

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)
    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Вложенное представление с возможностью записи

При работе с вложенными представлениями, которые поддерживают десериализованные данные, любые ошибки во вложенном объекте будут вложены под именем поля вложенного объекта.

serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}

такой же,.validated_dataСвойства будут содержать вложенные структуры данных.

писать для вложенного представления.create()метод

Если вы поддерживаете вложенные представления с возможностью записи, вам нужно будет написать обработчик, который обрабатывает сохранение нескольких объектов..create()или.update()метод.

В следующем примере показано, как обрабатывать создание пользователей с вложенными объектами профилей.

class UserSerializer(serializers.ModelSerializer):
    profile = ProfileSerializer()

    class Meta:
        model = User
        fields = ('username', 'email', 'profile')

    def create(self, validated_data):
        profile_data = validated_data.pop('profile')
        user = User.objects.create(**validated_data)
        Profile.objects.create(user=user, **profile_data)
        return user

писать для вложенного представления.update()метод

Для обновлений необходимо тщательно продумать, как обрабатывать реляционные обновления. Например, если данные для отношенияNoneили не предусмотрено, что из следующего должно произойти?

  • В базе данных установить отношение какNULL.
  • Удалить связанный экземпляр.
  • Не обращайте внимания на данные и оставьте как есть.
  • Выдает ошибку проверки.

Ниже приведен наш предыдущийUserSerializerв классе.update()Примеры методов.

    def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # Unless the application properly enforces that this field is
        # always set, the follow could raise a `DoesNotExist`, which
        # would need to be handled.
        profile = instance.profile

        instance.username = validated_data.get('username', instance.username)
        instance.email = validated_data.get('email', instance.email)
        instance.save()

        profile.is_premium_member = profile_data.get(
            'is_premium_member',
            profile.is_premium_member
        )
        profile.has_support_contract = profile_data.get(
            'has_support_contract',
            profile.has_support_contract
         )
        profile.save()

        return instance

Поскольку поведение вложенных операций создания и обновления может быть неоднозначным и может потребовать сложных зависимостей между связанными моделями, платформа REST 3 требует, чтобы вы всегда писали эти методы явно. дефолтModelSerializerиз.create()и.update()Метод не включает поддержку вложенных представлений с возможностью записи.

Однако доступны сторонние пакеты, например, поддерживающие автоматические вложенные представления с возможностью записи.DRF Writable Nested.

Сохраните соответствующий экземпляр в классе менеджера модели.

Другой способ сохранить несколько связанных экземпляров в сериализованном классе — написать собственный класс диспетчера моделей.

Например, предположим, что мы хотим убедиться, чтоUserэкземпляр иProfileЭкземпляры всегда создаются как пара. Мы могли бы написать собственный класс менеджера, как показано ниже:

class UserManager(models.Manager):
    ...

    def create(self, username, email, is_premium_member=False, has_support_contract=False):
        user = User(username=username, email=email)
        user.save()
        profile = Profile(
            user=user,
            is_premium_member=is_premium_member,
            has_support_contract=has_support_contract
        )
        profile.save()
        return user

Этот класс менеджера теперь лучше инкапсулирует то, что экземпляр пользователя и экземпляр профиля всегда создаются одновременно. Теперь можно переписать класс сериализации.create()чтобы использовать новый метод класса управления.

def create(self, validated_data):
    return User.objects.create(
        username=validated_data['username'],
        email=validated_data['email']
        is_premium_member=validated_data['profile']['is_premium_member']
        has_support_contract=validated_data['profile']['has_support_contract']
    )

Работа с несколькими объектами

SerializerКлассы также могут выполнять сериализацию или десериализацию списков объектов.

Сериализация нескольких объектов

Чтобы сериализовать набор запросов или список объектов вместо одного экземпляра объекта, при создании экземпляра сериализованного класса вы должны передатьmany=Trueлоготип. Затем вы можете передать набор запросов или список объектов для сериализации.

queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
#     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
#     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
#     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]

Десериализовать несколько объектов

Поведение по умолчанию при десериализации нескольких объектов заключается в поддержке создания нескольких объектов, но не нескольких обновлений объектов.

Включить дополнительный контекст

В дополнение к сериализуемому объекту бывают случаи, когда для сериализованного класса требуется дополнительный контекст. Распространенным случаем является то, что если вы используете класс сериализации, который содержит отношение гиперссылки, вам нужен класс сериализации для доступа к текущему запросу, чтобы он мог правильно сгенерировать полный URL-адрес.

При создании экземпляра сериализованного объекта вы можете предоставить любой дополнительный контекст, передав параметр контекста. Например:

serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}

посетивself.contextсвойств, контекстные словари можно использовать в любой логике сериализованного поля объекта, например в пользовательском.to_representation()метод.

ModelSerializer

Часто вам нужно, чтобы классы сериализации были тесно связаны с определениями модели Django.

ModelSerializerКласс предоставляет ярлык, позволяющий автоматически создаватьSerializerКласс с полями, соответствующими полям класса модели.

ModelSerializerкласс и рутинаSerializerКлассы те же, за исключением того, что:

  • Он автоматически генерирует набор полей на основе модели.
  • Он автоматически генерирует валидаторы для сериализованных классов, такие как валидатор unique_together.
  • это содержит.create()и.update()Простая реализация по умолчанию .

Объявите ModelSerializer следующим образом:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

По умолчанию все поля класса модели в этом классе будут сопоставлены с соответствующими полями сериализованного класса.

Любые отношения (например, внешний ключ в модели) будут сопоставлены сPrimaryKeyRelatedField. Обратные связи не включаются по умолчанию, если они не указаны в документации по отношениям сериализации.

экзаменModelSerializer

Класс сериализатора может генерировать строку представления, позволяющую полностью проверить состояние его полей. в настоящее время используетModelSerializerЭто особенно полезно при работе, когда вам нужно определить, какие поля и валидаторы он автоматически создает для вас.

Для этого используйтеpython manage.py shellПерейдите в оболочку Django, затем импортируйте класс сериализации, создайте его экземпляр и распечатайте представление объекта...

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_blank=True, max_length=100, required=False)
    owner = PrimaryKeyRelatedField(queryset=User.objects.all())

Укажите поля для включения

Если вы хотите использовать только подмножество полей по умолчанию в сериализаторе модели, вы можете использоватьfieldsилиexcludeвариант для этого, как и при использованииModelFormТакой же. Настоятельно рекомендуется явно использоватьfieldsВсе поля для сериализации свойств. Это снизит вероятность непреднамеренного раскрытия данных при изменении модели.

Возьмите каштан:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')

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

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__'

ты можешь поставитьexcludeДля свойства задан список полей, которые необходимо исключить из сериализатора.

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        exclude = ('users',)

В приведенном выше примере, еслиAccountМодель имеет 3 поляaccount_name,usersиcreated, появится полеaccount_nameиcreatedсериализуется.

fieldsиexcludeИмена в свойствах обычно сопоставляются с полями модели класса модели.

илиfieldsИмена в параметрах могут быть сопоставлены со свойствами или методами. не становясь параметром в классе модели.

Начиная с версии 3.3.0, одно из этих свойств должно быть предоставленоfieldsилиexclude.

указать вложенную сериализацию

дефолтModelSerializerИспользуйте первичные ключи для ассоциации, но вы также можете использоватьdepthВозможность легко генерировать вложенные представления (самоассоциация):

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        depth = 1

depthПараметры должны быть установлены на целочисленное значение, указывающее глубину ассоциации, которую необходимо пройти, прежде чем вернуться к плоскому представлению.

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

Явно указать поля

Вы можете добавить дополнительные поля вModelSerializerили переопределите поле по умолчанию, объявив поле в классе, как вы сделали сSerializerкласс делает.

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source='get_absolute_url', read_only=True)
    groups = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = Account

Дополнительные поля могут соответствовать любому свойству или вызываемому полю модели.

Укажите поля только для чтения

Вы можете назначить несколько полей только для чтения. Не добавляйте явно в каждое полеread_only = Trueсвойства, вы можете использовать мета-параметр ярлыкаread_only_fields.

Параметр должен быть списком или кортежем имен полей, объявленных следующим образом:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'users', 'created')
        read_only_fields = ('account_name',)

содержатьeditable = Falseполя модели,AutoFieldПоля доступны только для чтения по умолчанию и их не нужно добавлять вread_only_fieldsопции.

Уведомление: существует особый случай, когда поля только для чтения находятся на уровне модели.unique_togetherчасть ограничений. В этом случае класс сериализации требует проверки для ограничения поля, но пользователь также не может редактировать его.

Правильный способ справиться с этим — явно указать поля в классе сериализации, предоставив при этомread_only = Trueиdefault = ...аргументы ключевых слов.

Примером этого является текущая сертификация.Userотношения только для чтения с другим идентификатором, которыйunique_together. В этом случае вы должны объявить пользовательское поле следующим образом:

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

Я расскажу о проверке позже

Дополнительные аргументы ключевого слова

Существует также ярлык, который позволяет использоватьextra_kwargsПараметр указывает любые дополнительные аргументы ключевого слова в поле. иread_only_fieldsЭто означает, что вам не нужно явно объявлять поле в классе сериализатора.

Параметр представляет собой словарь, отображающий имена полей в словарь аргументов ключевых слов. Например:

class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('email', 'username', 'password')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User(
            email=validated_data['email'],
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

реляционное поле

При сериализации экземпляра модели можно выбрать несколько различных способов представления отношений.ModelSerializerПо умолчанию используется первичный ключ связанного экземпляра.

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

настраиваемое сопоставление полей

Класс ModelSerializer также предоставляет API, который можно переопределить для изменения полей сериализованного объекта по мере его создания.

Обычно, еслиModelSerializerНет возможности сгенерировать поля, требуемые по умолчанию, тогда вы должны добавить их в класс явно, или использовать обычныйSerializerсвоего рода. Однако в некоторых случаях может потребоваться создать новый базовый класс, определяющий, как создавать поля сериализованного объекта для данной модели.

.serializer_field_mapping

Сопоставление классов модели Django с классами сериализации фреймворка REST. Вы можете переопределить это сопоставление, чтобы изменить класс сериализации по умолчанию, который следует использовать для каждого класса модели.

.serializer_related_field

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

заModelSerializer, который по умолчанию равенPrimaryKeyRelatedField.

заHyperlinkedModelSerializer, который по умолчанию равенserializers.HyperlinkedRelatedField.

serializer_url_field

Класс поля Serializer, который следует использовать для сериализации любогоurlполе.

По умолчаниюserializers.HyperlinkedIdentityField.

serializer_choice_field

Класс поля сериализатора, который следует использовать для любых полей выбора в сериализаторе.

По умолчаниюserializers.ChoiceField.

API field_class и field_kwargs

Следующий метод вызывается для определения аргументов класса и ключевого слова для каждого поля, которое должно автоматически включаться в сериализатор. Все эти методы должны возвращать два кортежа(field_class, field_kwargs).

.build_standard_field(self, field_name, model_field)

Вызывается для создания полей сериализатора, которые сопоставляются с полями стандартной модели.

Реализация по умолчанию основана наserializer_field_mappingСвойство возвращает сериализованный класс.

.build_relational_field(self, field_name, relation_info)

Вызывается для создания полей сериализатора, которые сопоставляются с полями реляционной модели.

Реализация по умолчанию основана наserializer_relational_fieldСвойство возвращает сериализованный класс.

relation_infoПараметр представляет собой именованный кортеж, содержащийmodel_field,related_model,to_manyиhas_through_modelАтрибуты.

.build_nested_field(self, field_name, relation_info, nested_depth)

когдаdepthКогда параметр установлен, вызывается для создания полей сериализатора, которые сопоставляются с полями реляционной модели.

Реализация по умолчанию динамически создаетModelSerializerилиHyperlinkedModelSerializerВложенный класс сериализации.

nested_depthбудетdepthЗначение опции уменьшается на 1.

relation_infoПараметр представляет собой именованный кортеж, содержащийmodel_field,related_model,to_manyиhas_through_modelАтрибуты.

.build_property_field(self, field_name, model_class)

Вызывается для создания полей сериализатора, которые сопоставляются со свойствами или методами без аргументов в классе модели.

Реализация по умолчанию возвращаетReadOnlyFieldсвоего рода.

.build_url_field(self, field_name, model_class)

вызывается для собственного сериализатораurlПоле создает поле сериализатора.

Реализация по умолчанию возвращаетHyperlinkedIdentityFieldсвоего рода.

.build_unknown_field(self, field_name, model_class)

Вызывается, когда имя поля не соответствует какому-либо полю или свойству модели. Реализация по умолчанию выдает ошибку. Но подклассы могут настроить это поведение.

HyperlinkedModelSerializer

HyperlinkedModelSerializerкласс сModelSerializerКлассы похожи, за исключением того, что они используют гиперссылки для представления отношений вместо первичных ключей.

По умолчанию сериализатор будет содержатьurlполя вместо полей первичного ключа.

поле URL будет использоватьHyperlinkedIdentityFieldПоле сериализатора для представления, и любые отношения в модели будут использоватьHyperlinkedRelatedFieldПоле сериализатора для представления.

Вы можете сделать это, добавив первичный ключ вfieldsвозможность явного включения первичного ключа, например:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ('url', 'id', 'account_name', 'users', 'created')

Абсолютные и относительные URL-адреса

в инстанцированииHyperlinkedModelSerializer, текущий запрос должен быть включен в контекст сериализации, например:

serializer = AccountSerializer(queryset, context={'request': request})

Это гарантирует, что гиперссылка может содержать соответствующее имя хоста для создания полного URL-адреса, например:

http://api.example.com/accounts/1/

вместо относительного URL, например:

/accounts/1/

Если вы действительно хотите использовать относительные URL-адреса, вы должны явно передать их в контексте сериализации.{'request':None}.

Как определить вид гиперссылки

Необходимо определить, какие виды следует использовать для создания гиперссылок на экземпляры модели.

По умолчанию ожидается, что гиперссылки будут соответствовать стилям с'{model_name}-detail'соответствующее имя представления и передатьpkЭкземпляр поиска аргумента ключевого слова.

ты можешь использовать этоextra_kwargsв настройкахview_nameиlookup_fieldПараметры переопределяют имя представления поля URL-адреса и поля подстановки следующим образом:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ('account_url', 'account_name', 'users', 'created')
        extra_kwargs = {
            'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
            'users': {'lookup_field': 'username'}
        }

Кроме того, поля в сериализованном классе можно задать явно. Например:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='accounts',
        lookup_field='slug'
    )
    users = serializers.HyperlinkedRelatedField(
        view_name='user-detail',
        lookup_field='username',
        many=True,
        read_only=True
    )

    class Meta:
        model = Account
        fields = ('url', 'account_name', 'users', 'created')

Совет: правильное сопоставление гиперссылок и URL-адресов иногда может быть немного сложным. РаспечататьHyperlinkedModelSerializerпримерreprэто особенно полезный способ проверить, какие именно имена представлений и поля поиска должны отображаться в этих отношениях.

Изменить имя поля URL

Имя поля URL-адреса по умолчанию равно 'url'. можно использоватьURL_FIELD_NAME(в файле настроек) Переопределить этот параметр глобально.

ListSerializer

ListSerializerКласс обеспечивает поведение сериализации и одновременной проверки нескольких объектов. Обычно вам не нужно использовать напрямуюListSerializer, вместо этого его следует просто передать при создании экземпляра сериализованного классаmany=True.

Когда сериализованный класс создается иmany = Trueпроходит, аListSerializerэкземпляр будет создан. Сериализованный класс становится родительскимListSerializerребенок

Следующие параметры также могут быть переданы вListSerializerполе или прошлоmany = TrueКласс сериализации:

allow_empty

По умолчанию этоTrue, но если вы хотите запретить пустые списки в качестве допустимых входных данных, вы можете установить для него значениеFalse.

настроитьListSerializerповедение

Есть несколько ситуаций, когда может потребоваться настройкаListSerializerповедение. Например:

  • Хотите обеспечить определенную проверку списков, например проверку того, что один элемент не конфликтует с другим элементом в списке.
  • Хотите настроить поведение создания или обновления нескольких объектов.

В этих случаях это можно сделать с помощью класса сериализацииMetaв классеlist_serializer_classвозможность изменить переданныйmany=Trueкласс для использования.

class CustomListSerializer(serializers.ListSerializer):
    ...

class CustomSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = CustomListSerializer

Настройка создания нескольких объектов

Реализация по умолчанию для создания нескольких объектов заключается в простом вызове каждого элемента в списке..create(). Если вы хотите настроить это поведение, вам нужно передатьmany=TrueобычайListSerializerкласс.create()метод.

class BookListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        books = [Book(**item) for item in validated_data]
        return Book.objects.bulk_create(books)

class BookSerializer(serializers.Serializer):
    ...
    class Meta:
        list_serializer_class = BookListSerializer

Настройка обновления нескольких объектов

по умолчанию,ListSerializerКлассы не поддерживают обновления нескольких объектов. Это связано с тем, что ожидаемое поведение операций вставки и удаления неоднозначно.

Для поддержки многообъектных обновлений необходимо переопределить метод обновления. При написании кода обновления для нескольких объектов обязательно помните о следующем:

  • Как определить, какой экземпляр следует обновить для каждого элемента в списке данных?
  • Как следует обращаться со вставками? Являются ли они недействительными или создают новые объекты?
  • Как следует обрабатывать удаления? Подразумевают ли они удаление объекта или удаление отношения? Следует ли их игнорировать или они недействительны?
  • Как справиться с сортировкой? Означает ли изменение положения двух элементов изменение состояния или оно игнорируется?

Вам нужно добавить явный класс сериализации экземпляраidполе. Неявно сгенерированный по умолчаниюidПоле отмечено какread_only. Это приведет к его удалению при обновлении. Как только вы явно объявите его, он будет доступен в методе обновления класса сериализатора списка.

Вот пример того, как вы можете реализовать обновление нескольких объектов:

class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        # Maps for id->instance and id->data item.
        book_mapping = {book.id: book for book in instance}
        data_mapping = {item['id']: item for item in validated_data}

        # Perform creations and updates.
        ret = []
        for book_id, data in data_mapping.items():
            book = book_mapping.get(book_id, None)
            if book is None:
                ret.append(self.child.create(data))
            else:
                ret.append(self.child.update(book, data))

        # Perform deletions.
        for book_id, book in book_mapping.items():
            if book_id not in data_mapping:
                book.delete()

        return ret

class BookSerializer(serializers.Serializer):
    # We need to identify elements in the list using their primary key,
    # so use a writable field here, rather than the default which would be read-only.
    id = serializers.IntegerField()
    ...

    class Meta:
        list_serializer_class = BookListSerializer

Пользовательская инициализация ListSerializer

когда естьmany=TrueКогда создается экземпляр сериализованного класса, нам нужно определить, какие параметры и аргументы ключевых слов должны быть переданы дочерним элементам.Serializerкласс и родительListSerializerКатегория.__ init __()метод.

Реализация по умолчанию заключается в передаче всех параметров обоим классам, кромеvalidatorsи любые аргументы пользовательского ключевого слова, оба из которых, как предполагается, используются для классов субсериализации.

Иногда вам может понадобиться явно указать при передачеmany=TrueКак создавать экземпляры подклассов и суперклассов. ты можешь использовать этоmany_initметод класса для этого.

    @classmethod
    def many_init(cls, *args, **kwargs):
        # Instantiate the child serializer.
        kwargs['child'] = cls()
        # Instantiate the parent list serializer.
        return CustomListSerializer(*args, **kwargs)

BaseSerializer

BaseSerializerКлассы можно использовать для простой поддержки других стилей сериализации и десериализации.

Этот класс реализуетSerializerТот же базовый API, что и у класса:

  • .data- Возвращает исходящее необработанное представление.
  • .is_valid()- Десериализовать и проверять входящие данные.
  • .validated_data- Возвращает проверенные входящие данные.
  • .errors- возвращает ошибку при проверке.
  • .save()- Сохраните проверенные данные в экземпляре объекта.

Существует четыре метода, которые можно переопределить, в зависимости от того, какую функциональность вы хотите, чтобы ваш класс сериализации поддерживал:

  • .to_representation()- Переопределите эту операцию для поддержки сериализации для операций чтения.
  • .to_internal_value()- Переопределите эту операцию для поддержки десериализации операций записи.
  • .create() 和 .update()- Переопределить один или оба для поддержки сохранения экземпляров.

Поскольку этот класс предоставляетSerializerкласс с тем же интерфейсом, поэтому вы можете использовать существующий обычныйSerializerилиModelSerializerНапример, используйте его с общими представлениями на основе классов.

Единственная разница в том,BaseSerializerКласс не создает HTML-формы в доступном для просмотра API. Это связано с тем, что данные, которые они возвращают, не содержат всей информации о полях, которая позволила бы отображать каждое поле как соответствующий ввод HTML.

Read-only BaseSerializerсвоего рода

нужно использоватьBaseSerializerКласс реализует класс сериализации только для чтения, нам просто нужно переопределить.to_representation()метод. Давайте рассмотрим пример с использованием простой модели Django:

class HighScore(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    player_name = models.CharField(max_length=10)
    score = models.IntegerField()

создан дляHighScoreКласс сериализации только для чтения, который преобразует экземпляры в примитивные типы данных, очень прост.

class HighScoreSerializer(serializers.BaseSerializer):
    def to_representation(self, obj):
        return {
            'score': obj.score,
            'player_name': obj.player_name
        }

Теперь мы можем использовать этот класс для сериализации одногоHighScoreПример:

@api_view(['GET'])
def high_score(request, pk):
    instance = HighScore.objects.get(pk=pk)
    serializer = HighScoreSerializer(instance)
    return Response(serializer.data)

Или используйте его для сериализации нескольких экземпляров:

@api_view(['GET'])
def all_high_scores(request):
    queryset = HighScore.objects.order_by('-score')
    serializer = HighScoreSerializer(queryset, many=True)
    return Response(serializer.data)

Чтение-запись класса BaseSerializer

Чтобы создать класс сериализации для чтения и записи, нам сначала нужно реализовать.to_internal_value()метод. Этот метод возвращает проверочное значение, которое будет использоваться для создания экземпляра объекта, и может повышаться, если предоставленные данные имеют неправильный формат.ValidationError.

После реализации.to_internal_value(), базовый API проверки будет доступен в сериализаторе, и вы сможете использовать.is_valid(),.validated_dataи.errors.

Если вы все еще хотите поддержать.save(), также необходимо реализовать.create()и.update()один или оба метода.

Ниже приведен наш предыдущийHighScoreSerializerПолный пример , который был обновлен для поддержки операций чтения и записи.

class HighScoreSerializer(serializers.BaseSerializer):
    def to_internal_value(self, data):
        score = data.get('score')
        player_name = data.get('player_name')

        # Perform the data validation.
        if not score:
            raise ValidationError({
                'score': 'This field is required.'
            })
        if not player_name:
            raise ValidationError({
                'player_name': 'This field is required.'
            })
        if len(player_name) > 10:
            raise ValidationError({
                'player_name': 'May not be more than 10 characters.'
            })

        # Return the validated values. This will be available as
        # the `.validated_data` property.
        return {
            'score': int(score),
            'player_name': player_name
        }

    def to_representation(self, obj):
        return {
            'score': obj.score,
            'player_name': obj.player_name
        }

    def create(self, validated_data):
        return HighScore.objects.create(**validated_data)

Создать новый базовый класс

Если вы хотите реализовать новый общий класс сериализации для обработки определенного стиля сериализации или для интеграции с необязательным сервером хранения, тогдаBaseSerializerТакже полезны занятия.

Следующие классы являются примерами универсальных классов сериализации, которые могут обрабатывать приведение произвольных объектов к их базовому представлению.

class ObjectSerializer(serializers.BaseSerializer):
    """
    A read-only serializer that coerces arbitrary complex objects
    into primitive representations.
    """
    def to_representation(self, obj):
        for attribute_name in dir(obj):
            attribute = getattr(obj, attribute_name)
            if attribute_name('_'):
                # Ignore private attributes.
                pass
            elif hasattr(attribute, '__call__'):
                # Ignore methods and other callables.
                pass
            elif isinstance(attribute, (str, int, bool, float, type(None))):
                # Primitive types can be passed through unmodified.
                output[attribute_name] = attribute
            elif isinstance(attribute, list):
                # Recursively deal with items in lists.
                output[attribute_name] = [
                    self.to_representation(item) for item in attribute
                ]
            elif isinstance(attribute, dict):
                # Recursively deal with items in dictionaries.
                output[attribute_name] = {
                    str(key): self.to_representation(value)
                    for key, value in attribute.items()
                }
            else:
                # Force anything else to its string representation.
                output[attribute_name] = str(attribute)

Сериализатор использует расширенный

Переопределить поведение сериализации и десериализации

Если вам нужно изменить поведение сериализации или десериализации сериализованного класса, вы можете переопределить.to_representation()или.to_internal_value()метод достижения.

Эти два метода, возможно, придется переопределить по следующим причинам...

  • Добавьте новое поведение для нового базового класса сериализации.
  • Немного изменить поведение существующих классов.
  • Повысьте производительность сериализации для часто используемых конечных точек API, которые возвращают большие объемы данных.

Сигнатуры этих методов следующие:

.to_representation(self, obj)

Принимает экземпляр объекта, который необходимо сериализовать, и возвращает необработанное представление. Обычно это означает возврат структуры встроенных типов данных Python. Точные типы, которые можно обрабатывать, зависят от класса рендеринга, который вы настроили для API.

Может быть переопределен для изменения стиля презентации. Например:

def to_representation(self, instance):
    """Convert `username` to lowercase."""
    ret = super().to_representation(instance)
    ret['username'] = ret['username'].lower()
    return ret

.to_internal_value(self, data)

Принимает непроверенные входящие данные в качестве входных данных и должен возвращать какserializer.validated_dataПредоставлены данные проверки. Если вызывается в сериализованном классе.save(), возвращаемое значение также будет передано в.create()или.update()метод.

Если проверка не пройдена, метод выдаетserializers.ValidationError(errors).errorsПараметр должен быть полем, названным по имени поля (илиsettings.NON_FIELD_ERRORS_KEY) сопоставление со словарем списков сообщений об ошибках. Если вам не нужно изменять поведение десериализации, но вы хотите обеспечить проверку на уровне объекта, вместо этого рекомендуется переопределить.validate()метод.

перешли на этот методdataПараметры обычноrequest.data, поэтому тип предоставляемых данных будет зависеть от класса синтаксического анализатора, который вы настроили для API.

Наследовать класс сериализации

Подобно формам Django, вы можете расширять и повторно использовать классы сериализации посредством наследования. Это позволяет объявить общий набор полей или методов в родительском классе, который затем можно использовать в нескольких классах сериализации. Например,

class MyBaseSerializer(Serializer):
    my_field = serializers.CharField()

    def validate_my_field(self):
        ...

class MySerializer(MyBaseSerializer):
    ...

с ДжангоModelиModelFormКак и классы, сериализуйте внутренний класс в классеMetaКласс не начинается внутри своего родительского классаMetaНеявное наследование в классах. если ты хочешьMetaКласс наследует родительский класс и должен быть четко указан. Например:

class AccountSerializer(MyBaseSerializer):
    class Meta(MyBaseSerializer.Meta):
        model = Account

Обычно мы рекомендуем против внутреннегоMetaВ классе используется наследование, вместо этого явно объявляются все опции.

Динамически изменять поля

После инициализации класса сериализации вы можете использовать.fieldsСвойство обращается к набору словарей полей в классе сериализации. Получая доступ к этому свойству и изменяя его, можно достичь цели динамического изменения класса сериализации.

Изменить напрямуюfieldsПараметры позволяют делать интересные вещи, например изменять параметры сериализованных полей во время выполнения, а не при объявлении сериализованного класса.

Возьмите каштан:

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

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

Это позволит вам сделать следующее:

>>> class UserSerializer(DynamicFieldsModelSerializer):
>>>     class Meta:
>>>         model = User
>>>         fields = ('id', 'username', 'email')
>>>
>>> print UserSerializer(user)
{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
>>>
>>> print UserSerializer(user, fields=('id', 'email'))
{'id': 2, 'email': 'jon@example.com'}