Найти тему
Computer Pro

Руководство по сериализаторам в Django REST Framework. Часть 2

Оглавление

В этой статье подробно рассматриваются сериализаторы в Django REST Framework и объясняется, как они работают.

Просто картинка, для разбавления сухого технического повествования. Фото мое. Белое море. Восход.
Просто картинка, для разбавления сухого технического повествования. Фото мое. Белое море. Восход.

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

  • Первая часть посвящена APIView классу – основе конечных точек API в Django REST Framework.
  • Во второй части рассказывается о Serializer и ModelSerializer классах, а также объясняется их назначение и использование.
  • В третьей части рассматриваются GenericAPIView и его родственные классы, предназначенные для работы с моделями баз данных. Это классы, которые вы собираетесь использовать чаще всего.
  • В четвертой части рассказывается о ViewSet классе и его подклассах, которые позволяют создавать полный набор конечных точек всего несколькими строками кода.
  • Пятая часть описывает внутреннюю механику Django REST Framework и объясняет, что именно происходит во время запроса.

Предварительные условия и допущения

  • У вас есть базовые знания о Django Framework.
  • В вашей системе установлен Python 3.6+.
  • Установлены все необходимые зависимости
  • Имеется созданный django-проект с хотя бы одним приложением,
  • Выполнены все миграции

Что такое сериализатор в DRF?

Давайте определимся с термином "сериализатор". Вот что по этому поводу думает википедия:

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

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

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

Вместо этого сериализаторы в DRF работают с собственными типами данных Python. Они преобразуют объекты модели и наборы запросов в “упрощенное” представление, состоящее из строк, целых чисел, логических значений, словарей и списков.

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

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

Помимо преобразования данных в собственные типы данных, сериализаторы в DRF выполняют другие обязанности: проверяют предоставленные пользователем данные и работают с моделями баз данных.

Сериализаторы необходимы для Django REST Framework.

ВЫВОД: сериализаторы в DRF отвечают за следующее:
1. Преобразование экземпляров модели и наборов запросов в собственные типы данных Python.
2. Проверка данных, предоставленных пользователем.
3. Создание и обновление экземпляров моделей баз данных.

Как использовать сериализаторы DRF?

Если вы когда-либо работали с Form классом в Django, вы найдете знакомые сериализаторы DRF. Фактически, их архитектура основана на Django forms.

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

Чтобы определить, какие данные будет принимать и возвращать наша конечная точка, нам нужно будет создать класс сериализатора с соответствующими полями. Создайте новый файл с именем serializers.py и добавьте к нему следующий код:

-2

Мы объявили пару свойств в нашем классе и назначили field экземпляры. При этом поле email имеет отличное от поля password свойство. Дело в том что в поле EmailField имеется встроенную проверку, является ли введеный email адресом электронной почты.

Полный список полей сериализатора находится в документации на DRF.

Параметр required=True озачает то что пользователь обязан заполнить это поле, иначе эндпойнт вернет ошибку.

Сам по себе сериализатор не очень полезен. Нам нужен способ пересылать в него данные, отправленные пользователем. Именно за это отвечают APIViews, описанные в первой части.

Давайте объявим один из них. Откройте или создайте views.py и добавьте к нему следующие строки:

-3

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

-4

В браузере можно откркрыть конечную точку, что прописывали только что. Хотя там и будет написано что метод не поддерживается, но не пугайтесь, это он про метод GET, которым выполняется эндпойнт. Мы же сейчас выполним метод POST и если все нормально, то ответ будет 200 ОК.

-5

Нажимаем кнопку POST и браузер пишет ответ, запрос выполнен успешно:

-6

Похожий запрос с помощью консольного инструмента http:

-7

Но не все запросы будут содержать достоверные данные. Давайте теперь рассмотрим процесс проверки.

Как работает проверка?

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

  1. Проверка для каждого поля выполняется Field классами, о которых мы кратко говорили ранее.
  2. Проверка для каждого поля, выполняемая Serializer классом – подробнее об этом скоро.
  3. Проверка по каждому запросу выполняется Serializer классом. Это позволяет нам проверять несколько полей одновременно.

Давайте рассмотрим каждый пункт по отдельности.

Проверка для каждого поля, выполняемая полевыми классами

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

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

-8

Это приятно, но иногда требуется добавить пользовательское правило проверки, которое DRF не предоставляет "из коробки". Например, вам может потребоваться подтвердить номер телефона.

В этом случае вы можете создать пользовательскую функцию проверки и передать ее через validators=[ ] параметр при объявлении поля. Вот как это будет выглядеть:

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

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

Проверка для каждого поля, выполняемая классом Serializer

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

К счастью, мы можем объявить метод в нашем классе serializer, который начинается с validate_, за которым следует имя поля. Например validate_email. DRF автоматически подберет этот метод и запустит его как часть проверки.

Такие методы всегда принимают два параметра: self и value. Последний представляет собой значение поля, отправленное во время запроса.

Вот как выглядит реализация нашего пользовательского метода проверки для поля электронной почты:

-11
Результат выполнения программы если email совпадет с email пользователя уже зарегистрированного в базе данных
Результат выполнения программы если email совпадет с email пользователя уже зарегистрированного в базе данных

Проверка по каждому запросу, выполняемая классом Serializer

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

Для этого DRF позволяет нам объявить метод с именем validate в нашем классе serializer. Этот метод всегда будет принимать два параметра: self и attrs. Последнее будет содержать все отправленные (и предварительно проверенные валидаторами для каждого поля) данные.

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

Следующие несколько снимков показывают вносимые нами изменения. Прежде всего, мы меняем принятые поля:

-13
-14

-15

Результат работы программы если пользователь существует:

-16

Если мы не вводим email или username:

-17

Ну и если я не указал email, а только username, поле email будет составлено из username + @anymail.ru (какого нибудь шаблона).

-18

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

Создание экземпляров модели с помощью Serializer

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

Сериализаторы в DRF предоставляют специальный метод для этой цели, create() который мы можем переопределить, чтобы указать желаемое поведение. Он принимает два параметра: self и validated_data. Как следует из названия, последний будет содержать данные после того, как для него будет выполнена проверка.

Этот метод будет запущен только в том случае, если проверка прошла успешно.

-19
serializer.save() вызывает метод create в сериализаторе
serializer.save() вызывает метод create в сериализаторе
-21

Мы вызовем save() метод в нашем сериализаторе. Это специальный метод, используемый для сохранения изменений в базе данных. Он достаточно умен, чтобы знать, что мы хотим создать новый экземпляр, поэтому он вызовет наш create() метод под капотом.

Обновление экземпляров модели с помощью Serializer

Хотя это и не совсем соответствует нашему текущему сценарию, стоит упомянуть другой метод – update(). Он похож на create(), но используется для обновления существующих записей. Он принимает три параметра: self, instance и validated_data, где экземпляр является экземпляром модели.

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

Давайте переименуем наш сериализатор в просто "UserSerializer", чтобы он звучал более обобщенно, и добавим к нему update() метод:

В Pycharm если нажать Ctrl+F6 и переименовать, то изменения произойдут во всех файлах где используется это имя
В Pycharm если нажать Ctrl+F6 и переименовать, то изменения произойдут во всех файлах где используется это имя
-23

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

-24

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

Что такое ModelSerializer в DRF?

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

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

Давайте модифицируем наш класс serializer, чтобы использовать функциональность ModelSerializer. Прежде всего, нам обновляем наследование и повторно объявляем наши поля. На этот раз нам не нужно указывать поля одно за другим. Вместо этого мы объявим вложенный класс с именем Meta внутри нашего сериализатора. Внутри Meta класса мы можем указать модель, с которой будет работать наш сериализатор, и какие поля этой модели он должен обрабатывать. Вот как это делается:

-25

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

ПРИМЕЧАНИЕ: поля модели, объявленные без null=True и / или blank=True, будут обрабатываться в соответствии с требованиями ModelSerializer. Он также наследуется validators=[...] от поля model, включая пользовательские, которые вы добавили вручную.

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

-26

По умолчанию поле username будет установлено как обязательное, поскольку в модели оно объявлено ненулевым. Чтобы разрешить пользователям регистрироваться по электронной почте или имени пользователя, нам нужно отметить поле username необязательным. Это очень легко сделать с помощью extra_kwargs={...} свойства Meta класса.

-27

Нам больше не нужно вручную проверять, существует ли пользователь с указанным именем пользователя, поскольку это будет проверяться автоматически из-за unique=True ограничения поля model. Следовательно, мы можем безопасно удалить наш validate_username метод.

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

К счастью, его легко настроить. Нам нужно добавить error_messages ключ в наш username словарь в разделе extra_kwargs. Значение должно быть словарем, где ключом является код ошибки (в нашем случае “уникальный”), а значением - сообщение об ошибке.

Вот как выглядит настроенное сообщение об ошибке:

-28

Мы по-прежнему хотим сохранить validate_email метод, потому что поле email не имеет уникального ограничения.

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

Наконец, мы можем удалить методы create() и update(), потому что ModelSerializer для них предусмотрена встроенная логика.

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

-29
-30

Довольно аккуратно, не правда ли? И нам не нужно вносить никаких изменений в views.py файл.

Заключение

Мы рассмотрели, как Serializer класс работает в Django REST Framework и как заставить его работать с моделью. Хотя полезно знать, что происходит под капотом, мы бы не хотели писать так много кода каждый раз, когда создаем сериализатор. По крайней мере, это не DRY.

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

ЗЫ. Статья получилась довольно длинной, но оно того стоило, это позволило лично мне лучше понять сериализаторы. Я мог при написании статьи где-то ошибиться и если найдете ошибку - сообщите в комментариях я всё поправлю.

Хоть я и не Борис, но могу быть не прав!
Хоть я и не Борис, но могу быть не прав!