Как работает serializer в django rest framework

Обновлено: 07.07.2024

Фреймворк сериализации Django предоставляет механизм для «перевода» моделей Django в другие форматы. Обычно эти другие форматы основаны на тексте и используются для отправки данных Django по сети, но сериализатор может обрабатывать любой формат (текстовый или нет).

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

Сериализация данных ¶

На самом высоком уровне вы можете сериализовать данные следующим образом:

Аргументами serialize функции являются формат для сериализации данных (см. Форматы сериализации ) и формат QuerySet для сериализации. (Фактически, вторым аргументом может быть любой итератор, который возвращает экземпляры модели Django, но почти всегда это будет QuerySet).

django.core.serializers. get_serializer ( формат ) ¶

Вы также можете напрямую использовать объект сериализатора:

Вызов get_serializer() с неизвестным форматом вызовет django.core.serializers.SerializerDoesNotExist исключение.

Подмножество полей ¶

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

В этом примере будут сериализованы только атрибуты name и size каждой модели. Первичный ключ всегда сериализуется как pk элемент результирующего вывода; он никогда не появляется в fields детали.

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

Унаследованные модели ¶

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

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

Если вы сериализуете только модель ресторана:

поля сериализованного вывода будут содержать только serves_hot_dogs атрибут. name Атрибут базового класса будет игнорироваться.

Чтобы полностью сериализовать ваши Restaurant экземпляры, вам также необходимо сериализовать Place модели:

Десериализация данных ¶

Десериализация данных очень похожа на их сериализацию:

Как видите, deserialize функция принимает тот же аргумент формата serialize , что и строка или поток данных, и возвращает итератор.

Однако здесь все немного усложняется. Объекты, возвращаемые deserialize итератором , не являются обычными объектами Django. Вместо этого они являются специальными DeserializedObject экземплярами, которые обертывают созданный, но несохраненный объект и любые связанные данные о взаимосвязи.

Вызов DeserializedObject.save() сохраняет объект в базе данных.

Если pk атрибут в сериализованных данных не существует или имеет значение null, новый экземпляр будет сохранен в базе данных.

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

Другими словами, обычное использование - проверить десериализованные объекты, чтобы убедиться, что они «подходят» для сохранения, прежде чем делать это. Если вы доверяете своему источнику данных, вы можете вместо этого сохранить объект напрямую и двигаться дальше.

Сам объект Django можно проверить как deserialized_object.object . Если поля в сериализованных данных не существуют в модели, DeserializationError будет вызвано, если ignorenonexistent аргумент не передан как True :

Форматы сериализации ¶

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

Идентификатор Информация
xml Сериализуется в простой диалект XML и обратно.
json Сериализуется в JSON и обратно .
jsonl Сериализуется в JSONL и обратно .
yaml Сериализуется в YAML (YAML не является языком разметки). Этот сериализатор доступен, только если установлен PyYAML .

Базовый формат сериализации XML выглядит так:

Вся коллекция объектов, которая сериализована или десериализована, представлена <django-objects> тегом -tag, который содержит несколько <object> -элементов. Каждый такой объект имеет два атрибута: «pk» и «model», последний представлен именем приложения («sessions») и именем модели в нижнем регистре («session»), разделенных точкой.

Каждое поле объекта сериализуется как <field> -элемент, содержащий поля «тип» и «имя». Текстовое содержимое элемента представляет собой значение, которое следует сохранить.

Внешние ключи и другие реляционные поля обрабатываются немного иначе:

В этом примере мы указываем, что auth.Permission объект с PK 27 имеет внешний ключ для contenttypes.ContentType экземпляра с PK 9.

Этот пример связывает данного пользователя с моделями разрешений с PK 46 и 47.

Если сериализуемый контент содержит управляющие символы, которые не поддерживаются стандартом XML 1.0, сериализация завершится ошибкой с ValueError исключением. Прочтите также объяснение W3C HTML, XHTML, XML и управляющих кодов .

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

Форматирование здесь немного проще, чем с XML. Вся коллекция просто представлена ​​как массив, а объекты представлены объектами JSON с тремя свойствами: «pk», «model» и «fields». «Fields» - это снова объект, содержащий имя и значение каждого поля как свойство и значение свойства соответственно.

Внешние ключи имеют PK связанного объекта как значение свойства. Отношения ManyToMany сериализуются для модели, которая их определяет, и представлены в виде списка PK.

Имейте в виду, что не весь вывод Django можно передать без изменений json . Например, если у вас есть какой-то настраиваемый тип в сериализуемом объекте, вам придется написать для него собственный json кодировщик. Примерно так будет работать:

Затем вы можете перейти cls=LazyEncoder к serializers.serialize() функции:

Также обратите внимание, что GeoDjango предоставляет настраиваемый сериализатор GeoJSON .

Все данные теперь выгружаются с помощью Unicode. Если вам нужно предыдущее поведение, перейдите ensure_ascii=True к serializers.serialize() функции.

DjangoJSONEncoder ¶

Сериализатор JSON использует DjangoJSONEncoder для кодирования. Подкласс JSONEncoder , он обрабатывает следующие дополнительные типы:

datetime Строка формы YYYY-MM-DDTHH:mm:ss.sssZ или, YYYY-MM-DDTHH:mm:ss.sss+HH:MM как определено в ECMA-262 . date Строка формы, YYYY-MM-DD как определено в ECMA-262 . time Строка формы, HH:MM:ss.sss как определено в ECMA-262 . timedelta Строка, представляющая продолжительность, как определено в ISO-8601. Например, отображается как . timedelta(days=1, hours=2, seconds=3.4) 'P1DT02H00M03.400000S' Decimal , Promise ( django.utils.functional.lazy() объекты), UUID Строковое представление объекта.

JSONL ¶

JSONL расшифровывается как JSON Lines . В этом формате объекты разделяются новыми строками, и каждая строка содержит допустимый объект JSON. Сериализованные данные JSONL выглядят так:

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

Сериализация YAML очень похожа на JSON. Список объектов сериализуется как сопоставления последовательности с ключами «pk», «model» и «fields». Каждое поле снова является отображением с ключом, являющимся именем поля, а значением - значением:

Ссылочные поля снова представлены PK или последовательностью PK.

Все данные теперь выгружаются с помощью Unicode. Если вам нужно предыдущее поведение, перейдите allow_unicode=False к serializers.serialize() функции.

Натуральные ключи ¶

Стратегия сериализации по умолчанию для внешних ключей и отношений «многие ко многим» заключается в сериализации значения первичного ключа (ов) объектов в отношении. Эта стратегия хорошо работает для большинства объектов, но при некоторых обстоятельствах может вызвать затруднения.

Рассмотрим случай списка объектов, имеющих ссылку на внешний ключ ContentType . Если вы собираетесь сериализовать объект, который ссылается на тип контента, вам нужно иметь способ для начала ссылаться на этот тип контента. Поскольку ContentType объекты автоматически создаются Django в процессе синхронизации базы данных, предсказать первичный ключ данного типа контента нелегко; это будет зависеть от того, как и когда migrate было выполнено. Это верно для всех моделей , которые автоматически генерируют объекты, в частности , в том числе Permission , Group и User .

Никогда не следует включать автоматически сгенерированные объекты в фикстуру или другие сериализованные данные. По случайности первичные ключи в фикстуре могут совпадать с ключами в базе данных, и загрузка фикстуры не будет иметь никакого эффекта. В более вероятном случае, если они не совпадают, загрузка прибора завершится ошибкой с расширением IntegrityError .

Есть еще вопрос удобства. Целочисленный идентификатор - не всегда самый удобный способ ссылки на объект; иногда может быть полезна более естественная ссылка.

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

Десериализация естественных ключей ¶

Рассмотрим следующие две модели:

Обычно в сериализованных данных для Book ссылки на автора используется целое число. Например, в JSON книга может быть сериализована как:

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

Однако, если мы добавим к Person естественную обработку ключей, приспособление станет намного более человечным. Чтобы добавить обработку естественного ключа, вы определяете Manager по умолчанию для Person с get_by_natural_key() методом. В случае с человеком хорошим естественным ключом может быть пара имени и фамилии:

Теперь книги могут использовать этот естественный ключ для ссылки на Person объекты:

Когда вы пытаетесь загрузить эти сериализованные данные, Django будет использовать этот get_by_natural_key() метод для преобразования в первичный ключ реального объекта. ["Douglas", "Adams"] Person

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

Десериализация объектов без первичного ключа всегда будет проверять, есть ли у менеджера модели get_by_natural_key() метод, и если да, то использовать его для заполнения первичного ключа десериализованного объекта.

Сериализация естественных ключей ¶

Так как же заставить Django выдавать естественный ключ при сериализации объекта? Во-первых, вам нужно добавить еще один метод - на этот раз в саму модель:

Этот метод всегда должен возвращать кортеж с естественным ключом - в этом примере . Затем, когда вы звоните , вы предоставляете аргументы или : (first name, last name) serializers.serialize() use_natural_foreign_keys=True use_natural_primary_keys=True

Если use_natural_foreign_keys=True указано, Django будет использовать этот natural_key() метод для сериализации любой ссылки внешнего ключа на объекты того типа, который определяет метод.

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

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

Если вы используете dumpdata для генерации сериализованных данных, использовать и командную строку флагов для создания природных ключей. dumpdata --natural-foreign dumpdata --natural-primary

Вам не нужно определять оба natural_key() и get_by_natural_key() . Если вы не хотите, чтобы Django выводил естественные ключи во время сериализации, но вы хотите сохранить возможность загрузки естественных ключей, вы можете отказаться от реализации этого natural_key() метода.

И наоборот, если (по какой-то странной причине) вы хотите, чтобы Django выводил естественные ключи во время сериализации, но не имел возможности загружать эти ключевые значения, просто не определяйте get_by_natural_key() метод.

Естественные ключи и прямые ссылки ¶

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

Например, предположим, что в вашем приспособлении есть следующие объекты:

Типичное использование выглядит так:

Для того, чтобы это работало, объект ForeignKey на ссылающейся модели должен иметь null=True .

Зависимости при сериализации ¶

Часто можно избежать явной обработки прямых ссылок, позаботившись о порядке объектов в фикстуре.

Чтобы помочь с этим, вызовы, dumpdata которые используют этот параметр, будут сериализовать любую модель с помощью метода перед сериализацией стандартных объектов первичного ключа. dumpdata --natural-foreign natural_key()

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

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

Например, давайте добавим естественный ключ к Book модели из приведенного выше примера:

Естественный ключ для a Book - это комбинация его имени и автора. Это означает, что он Person должен быть сериализован раньше Book . Чтобы определить эту зависимость, мы добавляем одну дополнительную строку:

Это определение гарантирует, что все Person объекты будут сериализованы перед любыми Book объектами. В свою очередь, любой объект реферирование Book будет сериализовать после того, как Person и Book было сериализовать.


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

Предположим, вы создаете программное обеспечение для сайта электронной коммерции и у вас есть Заказ, в котором регистрируется покупка одного продукта кем-то по указанной цене, на определенную дату:

Теперь представьте, что вы хотите сохранить и получить данные о заказе из базы данных «ключ-значение». К счастью, его интерфейс принимает и возвращает словари, поэтому вам нужно преобразовать свой объект в словарь:

И если вы хотите получить некоторые данные из этой базы данных, вы можете получить данные dict и превратить их в свой объект Order:

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

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

Django поставляется с модулем сериализации, который позволяет «переводить» модели в другие форматы:

Он охватывает наиболее часто используемые случаи для веб-приложений, таких как JSON, YAML и XML. Но вы также можете использовать сторонние сериализаторы или создать свои собственные. Вам просто нужно зарегистрировать его в своем файле settings.py:

Чтобы создать свой собственный MyFormatSerializer , вам необходимо реализовать метод .serialize() и принять набор запросов и дополнительные параметры в качестве параметров:

Теперь вы можете сериализовать свой запрос в новый формат:

Вы можете использовать параметры options, чтобы определить поведение сериализатора. Например, если вы хотите определить, что вы собираетесь работать с вложенной сериализацией при работе с ForeignKeys , или вы просто хотите, чтобы эти данные возвращали свои первичные ключи, вы можете передать параметр flat=True в качестве опции и обработать его в своем методе:

Один из способов использования сериализации Django - это команды управления loaddata и dumpdata .

Сериализаторы DRF

В примере заказа вы можете создать сериализатор следующим образом:

И легко сериализуйте его данные:

Чтобы иметь возможность возвращать экземпляр из данных, вам необходимо реализовать два метода - create и update:

После этого вы можете создавать или обновлять экземпляры, вызывая is_valid() для проверки данных и save() для создания или обновления экземпляра:

Сериализаторы моделей

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

Вы можете создать для него сериализатор следующим образом:

Django автоматически включает в себя все модели поля в сериализатор и создает методы create и update .

Использование сериализаторов в представлениях на основе классов (CBV)

Как и формы с CBV от Django, сериализаторы хорошо интегрируются с DRF. Вы можете установить атрибут serializer_class так, чтобы сериализатор был доступен для представления:

Вы также можете определить метод get_serializer_class() :

В CBV есть и другие методы, которые взаимодействуют с сериализаторами. Например, get_serializer() возвращает уже созданный сериализатор, а get_serializer_context() возвращает аргументы, которые вы передадите сериализатору при создании его экземпляра.

В прошлой части мы в общих чертах рассмотрели, как устроен REST API на DRF при работе на чтение. Едва ли не самый сложный для понимания этап — сериализация. Вооружившись исходным кодом, полностью разберем этот этап — от приема набора записей из модели до их преобразования в список словарей.

Важный момент: мы говорим о работе сериалайзера только на чтение, то есть когда он отдаёт пользователю информацию из базы данных (БД) сайта. О работе на запись, когда данные поступают извне и их надо сохранить в БД, расскажем в следующей статье.

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

Как создаётся сериалайзер, работающий на чтение

Создание экземпляра сериалайзера мы описывали следующим образом:

Благодаря many=True запускается метод many_init класса BaseSerializer .

Подробнее о методе many_init :

  • При создании экземпляра сериалайзера он меняет родительский класс. Теперь родителем выступает не CapitalSerializer , а класс DRF для обработки наборов записей restframework.serializers.ListSerializer .
  • Созданный экземпляр сериалайзера наделяется атрибутом child . В него включается дочерний сериалайзер — экземпляр класса CapitalSerializer.

Помимо many=True мы передали значение для атрибута instance (инстанс). В нём — набор записей из модели.

Важное замечание: чтобы не запутаться и понимать, когда речь идёт о сериалайзере в целом, а когда — о дочернем сериалайзере, далее по тексту мы будем говорить «основной сериалайзер» (в коде контроллера это serializer_for_queryset ) и «дочерний сериалайзер» (атрибут child основного сериалайзера).

После создания основного сериалайзера мы обращаемся к его атрибуту data :

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

Что под капотом атрибута data основного сериалайзера

Важное замечание: атрибут data есть и у основного, и у дочернего сериалайзеров. Поэтому, чтобы найти подходящий исходный код, нужно помнить: экземпляр основного ( serializer_for_queryset ) относится к классу ListSerializer .

Задействован атрибут data родительского BaseSerializer . Исходный код:

Поскольку никакие данные ещё не сгенерированы (нет атрибута _data ), ничего не валидируется (нет _errors ), но есть инстанс (набор записей для сериализации), запускается метод to_representation , который и обрабатывает набор записей из модели.

Как работает метод to_represantation основного сериалайзера

Возвращаемся в класс ListSerializer .

Сделаем небольшую остановку:

  • Чтобы обрабатывать не одну запись из БД, а набор, при создании сериалайзера нужно указать many=True .
  • В этом случае мы получим матрёшку — основной сериалайзер с дочерним внутри.
  • Задача основного сериалайзера (он относится к классу ListSerializer ) — запустить цикл, в ходе которого дочерний обработает каждую запись и превратит ее в словарь.

Как работает метод to_representation дочернего сериалайзера

Дочерний сериалайзер — экземпляр класса CapitalSerializer — наследует от restframework.serializers.Serializer .

Пойдём по порядку: сначала создаётся пустой OrderedDict , далее идёт обращение к атрибуту _readable_fields .

Откуда берётся _readable_fields ? Смотрим исходный код:

То есть _readable_fields — это генератор, включающий поля дочернего сериалайзера, у которых нет атрибутa write_only со значением True . По умолчанию он False . Если объявить True , поле будет работать только на создание или обновление записи, но будет игнорироваться при её представлении.

В дочернем сериалайзере все поля могут работать на чтение (представление) — ограничений write only не установлено. Это значит, что генератор _readable_fields будет включать три поля — capital_city , capital_population , author .

Читаем код to_representation далее: генератор _readable_fields помещается в цикл, и у каждого поля вызывается метод get_attribute .

Если посмотреть код to_representation дальше, видно, что у поля вызывается и другой метод — to_representation . Это не опечатка: метод to_representation под одним и тем же названием, но с разной логикой:

  • есть у основного сериалайзера в классе ListSerializer ;
  • у дочернего сериалайзера в классе Serializer ;
  • у каждого поля дочернего сериалайзера в классе соответствующего поля.

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

Как запись из модели обрабатывается методами полей сериалайзера

Метод get_attribute работает с инстансом (instance). Важно не путать этот инстанс с инстансом основного сериалайзера. Инстанс основного сериалайзера — это набор записей из модели. Инстанс дочернего сериалайзера — каждая конкретная запись.

Вспомним строку из кода to_representation основного сериалайзера:

Этот item (отдельная запись из набора) и есть инстанс, с которым работает метод get_attribute конкретного поля.

Вызывается функция get_attribute , описанная на уровне всего модуля rest_framework.fields . Функция получает на вход запись из модели и значение атрибута поля source_attrs . Это список, который возникает в результате применения метода split (разделитель — точка) к строке, которая передавалась в аргументе source при создании поля. Если такой аргумент не передавали, то в качестве source будет взято имя поля.

Если вспомнить, как работает строковый метод split , станет понятно, что если при указании source не применялась точечная нотация, то список всегда будет из одного элемента.

У нас есть такие поля:

Получается следующая картина:

Как мы уже указывали, список source_attrs в качестве аргумента attrs передаётся в метод get_attribute rest_framework.fields :

Для полей capital_city и capital_population цикл for attr in attrs отработает однократно и выполнит инструкцию instance = getattr(instance, attr) . Встроенная Python-функция getattr извлекает из объекта записи (instance) значение, присвоенное конкретному атрибуту ( attr ) этого объекта.
При обработке записей из нашей таблицы рассматриваемую строку исходного кода можно представить примерно так:

  • На первой итерации инстанс — это объект записи из модели Capital. Из source_attrs берётся первый элемент author , и значение одноимённого атрибута становится новым инстансом. author — объект из модели User, с которой Capital связана через внешний ключ.
  • На следующей итерации из source_attrs берётся второй элемент username . Значение атрибута username будет взято уже от нового инстанса — объекта author . Так мы и получаем имя автора.

Извлечённые из объекта табличной записи данные помещаются в упорядоченный словарь ret , но перед этим с ними работает метод to_representation поля сериалайзера:

Задача метода to_representation — представить извлечённые из записи данные в определённом виде. Например, если поле сериалайзера относится к классу CharField , то извлечённые данные будут приведены к строке, а если IntegerField — к целому числу.

В нашем случае применение to_representation по сути ничего не даст. Например, из поля табличной записи capital_city будет извлечена строка. Метод to_representation поля CharField к извлечённой строке применит метод str . Очевидно, что строка останется строкой, то есть какого-то реального преобразования не произойдёт. Но если бы из поля табличной записи IntegerField извлекались целые числа и передавались полю класса CharField , то в итоге они превращались бы в строки.

При необходимости можно создать собственный класс поля сериалайзера, описать специфичную логику и для метода get_attribute , и для метода to_representation , чтобы как угодно преобразовывать поступившие на сериализацию данные. Примеры есть в документации — кастомные классы ColorField и ClassNameField .

Суммируем всё, что узнали

Преобразованный набор записей из Django-модели доступен в атрибуте data основного сериалайзера. При обращении к этому атрибуту задействуются следующие методы и атрибуты из-под капота DRF (разумеется, эти методы можно переопределить):

Метод, атрибут, функция Класс, модуль Действие
data serializers.BaseSerializer Запускает метод to_representation основного сериалайзера.
to_representation serializers.ListSerializer Запускает цикл, в ходе которого к каждой записи из набора применяется метод to_representation дочернего сериалайзера.
to_representation serializers.Serializer Сначала создаётся экземпляр упорядоченного словаря, пока он пустой. Далее запускается цикл по всем полям сериалайзера, у которых не выставлено write_only=True .
get_attribute fields (вызывается методом get_attribute класса fields.Field ) Функция стыкует поле сериалайзера с полем записи из БД. По умолчанию идет поиск поля, чьё название совпадает с названием поля сериалайзера. Если передавался аргумент source , сопоставление будет идти со значением этого аргумента. Из найденного поля табличной записи извлекается значение — текст, числа и т.д.
to_representation fields.КлассПоляКонкретногоТипа Извлечённое значение преобразуется согласно логике рассматриваемого метода. У каждого поля restframework она своя. Можно создать собственный класс поля и наделить его метод to_representation любой нужной логикой.

В словарь заносится пара «ключ-значение»:

  • ключ — название поля сериалайзера;
  • значение — данные, возвращённые методом to_representation поля сериалайзера.

Итог: список из OrderedDict в количестве, равном числу переданных и сериализованных записей из модели.

Надеюсь, статья оказалась полезной и позволила дать картину того, как под капотом DRF происходит сериализация данных из БД. Если у вас остались вопросы, задавайте их в комментариях — разберёмся вместе.

Для чтения данной статьи потребуются базовые знания по Django REST фреймворку.

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

Мы обсудим следующие вопросы:

  • несколько способов использовать аргумент source сериализатора;
  • как и когда использовать метод SerializerMethodField ;
  • как и когда использовать метод to_representation ;

Как использовать ключевой аргумент source

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

Давайте напишем сериализатор, способный создавать серийные представления класса User :

Теперь давайте произведем эту сериализацию:

Представим, что во фронтенде или в мобильном приложении, использующем такой API, требуется, чтобы ключ в серийном представлении был user_name вместо username .

Чтобы добиться этого, мы можем добавить в код метод CharField с ключевым аргументом source :

Давайте убедимся, что поле username заменилось на user_name в Meta.fields . Перезапустим оболочку и снова создадим экземпляр сериализатора:

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

В классе User есть метод под названием get_full_name . Давайте установим в нем поля first_name и last_name .

Давайте добавим в сериализатор поле под названием full_name . Свяжем аргумент source c User.get_full_name .

Теперь перезапустим оболочку и получим сериализованное представление класса User :

Обратите внимание, каким образом в поле full_name появляется желаемый результат. Под капотом DRF использует метод get_full_name для заполнения поля full_name .

Аргумент source также может без проблем взаимодействовать с отношениями, то есть с ForeignKey , OneToOneField и ManyToMany .

Представим, у нас есть модель Profile , которая имеет отношения один-к-одному ( OneToOneField ) с классом User .

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

Допустим, мы хотим, чтобы названия улицы и города отправлялись в сериализованном представлении. Тогда мы могли бы добавить поле улицы и поле города в сериализатор и передать их в аргумент source .

Давайте перезапустим оболочку и посмотрим на результат:

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

Мы хотим получить полный адрес пользователей, доступный с помощью метода user.profile.get_full_address () .

В данном случае мы можем просто передать в аргумент source метод profile.get_full_address .

Опять сериализуем класс User и получим:

Теперь давайте посмотрим, как легко работает аргумент source с отношением многие-ко-многим ( ManyToManyField ).

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

Для каждой группы понадобятся поля id и name .

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

Выглядеть он будет следующим образом:

А способ, отвечающий духу DRF, состоит в том, чтобы добавить поле all_groups в сериализатор и установить его значение при помощи класса GroupSerializer .

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

DRF достаточно умен, чтобы вызвать user.groups.all() , хотя мы просто установили аргумент source = groups . DRF заключает, что раз groups имеет тип ManyRelatedManager , то для получения всех связанных групп надо использовать .all() .

Если мы не хотим предоставлять групповую информацию по POST-запросу, мы можем добавить ключевой аргумент read_only=True в GroupSerializer .

Допустим, у нас есть модель под названием Article и она имеет отношение один-ко-многим ( ForeignKey ) к классу User . Тогда мы можем добавлять статьи (articles) пользователя (User) в сериализованное представление следующим образом:

Как и когда использовать метод SerializerMethodField

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

Вот несколько примеров:

Рассмотрим первый сценарий. Мы хотим изменить значение поля first_name в регистр заголовка (написание с заглавной буквы) в процессе сериализации.

До сих пор мы не хотели запускать никакой собственный код для поля first_name , поэтому наличия first_name в полях Meta.fields было достаточно. Сейчас мы хотим запустить некоторый собственный код, поэтому нам нужно будет явно определить поле first_name как экземпляр класса SerializerMethodField .

Когда поле имеет тип SerializerMethodField , DRF вызывает метод get_ при вычислении значения для этого поля. Аргумент obj здесь является экземпляром класса user .

Перезапустим оболочку и произведем сериализацию:

Обратите внимание, что имя (значение поля first_name ) теперь пишется с заглавной буквы.

Если мы хотим преобразовать значение поля full_name в верхний регистр, нам нужно будет изменить сериализатор следующим образом:

Опять произведем сериализацию и увидим:

Если же мы захотим установить значение groups в None вместо пустого списка, наш сериализатор примет следующий вид:

Как и когда использовать метод to_representation

Сериализаторы в DRF имеют полезный метод под названием to_representation .

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

Тогда наш сериализатор будет иметь следующий вид:

Аргумент instance является экземпляром класса User .

Давайте проведем сериализацию для суперюзера.

А теперь отметим пользователя как не-суперюзера и сделаем тоже самое:

Обратите внимание, что ключ admin отсутствует в сериализованных данных для не-суперюзера.

Выводы

Мы рассмотрели поведение сериализатора Django при чтении. Если вы хотите разобраться, как эффективно использовать сериализаторы при операция записи, ждите продолжение.

Читайте также: