django обновление данных через форму

Django обновление данных через форму

Рассмотрим ряд операций на примере модели Person :

Добавление данных

create

Для добавления данных применяется метод create() :

Получение из бд

Получение одного объекта

Метод get() возвращает один объект по определенному условию, которое передается в качестве параметра:

Метод get_or_create() возвращает объект, а если его нет в бд, то добавляет в бд новый объект.

Метод возвращает добавленный объект (в данном случае переменная bob) и булевое значение (created), которое хранит True, если добавление прошло успешно.

Если необходимо получить все имеющиеся объекты, то применяется метод all() :

filter()

exclude()

Метод exclude() позволяют исключить из выборки записи, которые соответвуют переданному в качестве параметра критерию:

Можно комбинировать два выше рассмотренных метода:

in_bulk()

Обновление

Для обновления объекта также применяется метод save() :

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

Это позволит повысить производительность.

update()

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

Иногда бывает необходимо изменить значение столбца в бд на основании уже имеющегося значения. В этом случае мы можем использовать функцию F() :

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

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

Если надо обновить вообще все записи, вне зависимости от условия, то необходимо комбинировать метод update с методом all() :

update_or_create()

Метод update_or_create обновляет запись, а если ее нет, то добавляет ее в таблицу:

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

Удаление

Для удаления мы можем вызвать метод delete() у удаляемого объекта:

Если не требуется получение отдельного объекта из базы данных, тогда можно удалить объект с помощью комбинации методов filter() и delete() :

Просмотр строки запроса

С помощью свойства query у результата запроса мы можем получить SQL-запрос, который выполнялся. Например:

Данный код отобразит на консоли SQL-запрос типа следующего:

Источник

Руководство часть 9: Работа с формами

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

Необходимые условия: Завершите все предыдущие учебные темы, в том числе Django руководство часть 8: Аутентификация пользователя и права доступа.
Цель: Научиться понимать, как создавать формы, чтобы получать информацию от пользователей и обновлять базу данных. Узнать, как обобщённые классы отображения форм могут значительно упростить процесс создания форм при работе с одной моделью.

Обзор

Пока что мы не создавали каких-либо форм в этом учебнике, но мы встречались с ними в административной панели Django — например, снимок экрана ниже показывает форму для редактирования одной из наших моделей книг (Book), состоящую из нескольких списков выбора и текстовых редакторов.

admin book add

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

Формы HTML

Начнём мы с краткого обзора Форм HTML. Рассмотрим простую форму HTML, имеющую поле для ввода имени некоторой «команды» («team»), и, связанную с данным полем, текстовой меткой:

Элемент input с type=»submit» будет показана как кнопка (по умолчанию), нажав на которую, пользователь отправляет введённые им данные на сервер (в данном случае только значение поля с идентификатором team_name ). Атрибуты формы определяют каким методом будут отправлены данные на сервер (атрибут method ) и куда (атрибут action ):

Ролью сервера в первую очередь является отрисовка начального состояния формы — либо содержащей пустые поля, либо с установленными начальными значениями. После того как пользователь нажмёт на кнопку, сервер получит все данные формы, а затем должен провести их валидацию. В том случае, если форма содержит неверные данные, сервер должен снова отрисовать форму, показав при этом поля с правильными данными, а также сообщения, описывающие «что именно пошло не так». В тот момент, когда сервер получит запрос с «правильными» данными он должен выполнить все необходимые действия (например, сохранение данных, возврат результата поиска, загрузка файла и тому подобное), а затем, в случае необходимости, проинформировать пользователя.

Как вы видите, создание HTML-формы, валидация и возврат данных, переотрисовка введённых значений, при необходимости, а также выполнение желаемых действий с «правильными данными», в целом, может потребовать довольно больших усилий для того, чтобы все «заработало». Django делает этот процесс намного проще, беря на себя некоторые «тяжёлые» и повторяющиеся участки кода!

Процесс управления формой в Django

Управление формами в Django использует те же самые техники, которые мы изучали в предыдущих частях руководства (при показе информации из наших моделей): отображение получает запрос, выполняет необходимые действия, включающие в себя чтение данных из моделей, генерацию и возврат страницы HTML (из шаблона, в который передаётся контекст, содержащий данные, которые и будут показаны). Что делает данный процесс более сложным, так это то, что серверной части надо дополнительно обработать данные, предоставленные пользователем и, в случае возникновения ошибок, снова перерисовать страницу.

Диаграмма, представленная ниже, демонстрирует процесс работы с формой в Django, начиная с запроса страницы, содержащей форму (выделено зелёным цветом).

Form%20Handling%20 %20Standard

В соответствии с данной диаграммой, главными моментами, которые берут на себя формы Django являются:

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

HTML-форма обновления книги. Класс Form и функция отображения

Класс Form

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

Объявление класса формы Form

Поля формы

Общие аргументы для большинства полей перечислены ниже:

Валидация

В целом, это все, что нам понадобится для создания формы в данном примере!

Копирование класса формы

Создайте и откройте файл locallibrary/catalog/forms.py, а затем скопируйте в него весь код, указанный в предыдущем фрагменте.

Конфигурация URL-адресов

Перед созданием отображения давайте добавим соответствующую конфигурацию URL-адреса для страницы обновления книг. Скопируйте следующий фрагмент в нижнюю часть файла locallibrary/catalog/urls.py.

Примечание: Вместо имени «pk» мы можем использовать любое другое, по нашему желанию, потому что мы имеем полный контроль над функцией отображения (которого у нас нет в случае использования встроенного обобщённого класса отображения, который ожидает параметр с определённым именем). Тем не менее имя pk является понятным сокращением от «primary key», поэтому мы его тут и используем!

Отображение

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

В первую очередь мы импортируем наш класс формы ( RenewBookForm ), а также другие необходимые объекты и методы:

Важно: Хотя вы также можете получить доступ к данным формы непосредственно через запрос (например request.POST[‘renewal_date’], или request.GET[‘renewal_date’] (в случае GET-запроса), это НЕ рекомендуется. Очищенные данные проверены на вредоносность и преобразованы в типы, совместимые с Python.

Последним шагом в части обработки формы представления является перенаправление на другую страницу, обычно страницу «Успех». В нашем случае мы используем объект класса HttpResponseRedirect и функцию reverse() для перехода к отображению с именем ‘all-borrowed’ (это было домашним заданием в Руководство часть 8: Аутентификация и разграничение доступа). Если вы не создали данную страницу, то просто укажите переход на домашнюю страницу сайта по адресу ‘/’).

Окончательный вид отображения показан ниже. Пожалуйста, скопируйте данный текст в нижнюю часть файла locallibrary/catalog/views.py.

Шаблон

Создайте шаблон, на который ссылается наше отображение (/catalog/templates/catalog/book_renew_librarian.html) и скопируйте в него код, указанный ниже:

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

Другие варианты применения переменной шаблона form

В простом случае применения <

> как показано выше, каждое поле рендерится в виде отдельной строки таблицы. Кроме того, вы можете отрендерить каждое поле как список элементов ( <> ), или как параграф ( <> ).

Что ещё больше вдохновляет, так это то, что вы можете полностью контролировать процесс рендеринга любой части формы, используя для этого дот-нотацию (точку). Например, мы можем получить доступ к следующим полям поля формы renewal_date :

Примеры того как вручную отрендерить формы в шаблонах, а также пробежать циклом по шаблонным полям, смотрите Работы с формами > Ручная работа с формами (Django docs).

Тестирование страницы

Если вы выполнили задание в Django руководство часть 8: Аутентификация и разрешение доступа, то у вас должна быть страница со списком всех книг в наличии библиотеки и данный список (страница) должен быть доступен только её сотрудникам. На данной странице в каждом пункте (для каждой книги) мы можем добавить ссылку на нашу новую страницу обновления книги.

Примечание: Помните что, для того чтобы перейти на страницу обновления книги, ваш тестовый логин должен иметь разрешение доступа типа » catalog.can_mark_returned «(возможно надо воспользоваться вашим аккаунтом для суперпользователя).

Вы можете попробовать вручную создать URL-адрес для тестирования, например — /renew/» rel=» noopener»>http://127.0.0.1:8000/catalog/book/ /renew/ (правильный идентификатор записи id для bookinstance можно получить, если перейти на страницу детальной информации книги и скопировать поле id ).

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

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

А такой наша форма будет в случае ввода неправильной даты:

Список всех книг с ссылками на страницу обновления данных:

Класс ModelForm

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

Примечание: Это не выглядит сильно проще, чем просто использовать класс Form (и это действительно так, поскольку мы используем только одно поле). Тем не менее, если вы хотите иметь много полей, то такой способ построения формы может значительно уменьшить количество кода и ускорить разработку!

Обобщённые классы отображения для редактирования

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

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

Отображения

Откройте файл отображений (locallibrary/catalog/views.py) и добавьте следующий код в его нижнюю часть:

Для случаев «создать» и «обновить» вам также понадобится определить поля для показа на форме (применяя тот же синтаксис, что и для ModelForm ). В этом случае мы демонстрируем синтаксис и для показа «всех» полей, и перечисление их по отдельности. Также вы можете указать начальные значения для каждого поля, применяя словарь пар имя_поля/значение (в целях демонстрации, в нашем примере мы явно указываем дату смерти — если хотите, то вы можете удалить это поле). По умолчанию отображения перенаправляют пользователя на страницу «успеха», показывая только что созданные/отредактированные данные (записи в модели). В нашем случае это, созданная в предыдущей части руководства, подробная информация об авторе. Вы можете указать альтернативное перенаправление при помощи параметра success_url (как в примере с классом AuthorDelete ).

Шаблоны

Отображения «создать» и «обновить» используют шаблоны с именем model_name_form.html, по умолчанию: (вы можете поменять суффикс на что-нибудь другое, при помощи поля template_name_suffix в вашем отображении, например, template_name_suffix = ‘_other_suffix’ )

Создайте файл шаблона locallibrary/catalog/templates/catalog/author_form.html и скопируйте в него следующий текст.

Это напоминает наши предыдущие формы и рендер полей при помощи таблицы. Заметьте, что мы снова используем <% csrf_token %>.

Отображения «удалить» ожидает «найти» шаблон с именем формата model_name_confirm_delete.html (и снова, вы можете изменить суффикс при помощи поля отображения template_name_suffix ). Создайте файл шаблона locallibrary/catalog/templates/catalog/author_confirm_delete.html и скопируйте в него текст, указанный ниже.

Настройки URL-адресов

Откройте файл конфигураций URL-адресов (locallibrary/catalog/urls.py) и добавьте в его нижнюю часть следующие настройки:

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

Примечание: Наблюдательные пользователи могли заметить, что мы ничего не делаем, чтобы предотвратить несанкционированный доступ к страницам! Мы оставили это в качестве упражнения для вас (подсказка: вы можете использовать PermissionRequiredMixin и, либо создать новое разрешение, или воспользоваться нашим прежним can_mark_returned ).

Тестирование страницы

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

Затем перейдите на страницу создания новой записи автора: http://127.0.0.1:8000/catalog/author/create/, которая должна быть похожей на следующий скриншот.

Введите в поля значения и нажмите на кнопку Submit, чтобы сохранить новую запись об авторе. После этого, вы должны были перейти на страницу редактирования только что созданного автора, имеющий адрес, похожий на следующий http://127.0.0.1:8000/catalog/author/10.

У вас есть возможность редактирования записей при помощи добавления /update/ в конец адреса подробной информации (то есть, http://127.0.0.1:8000/catalog/author/10/update/) — мы не показываем скриншот, потому что он выглядит в точности также как страница «создать»!

И последнее, мы можем удалить страницу, добавляя строку /delete/ в конец адреса подробной информации автора (то есть, http://127.0.0.1:8000/catalog/author/10/delete/). Django должен показать страницу, которая похожа на представленную ниже. Нажмите Yes, delete., чтобы удалить запись и перейти на страницу со списком авторов.

Проверьте себя

Итоги

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

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

Источник

Django Rest Framework для начинающих: создаём API для записи и обновления данных (часть 1)

Продолжаем изучать Django Rest Framework с точки зрения новичка. Мы уже разобрали создание REST API для получения данных из БД, включая отдельную статью о работе сериалайзера.

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

image loader

DRF позволяет не только извлекать и передавать записи из БД сторонним приложениям, но и принимать от них данные для использования на вашем веб-сайте. Например, чтобы создать новую запись в БД или обновить существующую. Когда REST API принимает данные извне, происходит их десериализация ― восстановление Python-объекта из последовательности байтов, пришедших по сети.

Процесс создания или обновления одной записи в БД с помощью DRF включает следующие шаги:

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

2. Стороннее приложение отправляет POST-, PUT- или PATCH-запрос к эндпоинту API.

3. Контроллер (view), отвечающий за эндпоинт, извлекает из атрибута data объекта request данные для записи.

4. В контроллере создаём экземпляр сериалайзера, которому передаём поступившие данные, а также при необходимости запись из БД, которую предстоит обновить, и другие аргументы.

5. Вызываем метод is_valid сериалайзера. Он валидирует данные, а также позволяет скорректировать и расширить их. При валидации используются как инструменты из-под капота, так и наши собственные методы.

6. При успешной валидации вызываем метод save сериалайзера, благодаря которому в БД создаётся новая запись или обновляется существующая.

Одной статьи для подробного разбора, увы, не хватит, поэтому я снова разделил её на две части. В первой части поговорим о создании и работе сериалайзера на запись — это шаги 1, 3 и 5. В следующей статье рассмотрим остальные шаги и проиллюстрируем работу API на примерах.

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

Объявляем класс сериалайзера на запись

Чтобы сериалайзер мог работать на запись, у него должны быть:

Напомню, что один и тот же класс сериалайзера может работать одновременно и на запись, и на чтение. Можно сделать и разные сериалайзеры под разные запросы.

Попробую пояснить на примере из документации:

Сериалайзер может работать на чтение, преобразовывая каждую переданную из БД запись, у которой есть атрибуты title и text, в словарь . Если атрибутов title или text у записи не окажется, возникнет исключение.

Этот же сериалайзер может работать на запись — только нужно дописать методы create и update. Тогда на вход он будет ожидать словарь . Если таких ключей в словаре не окажется, по ним будут пустые значения или по ключу title будет строка длиной более 100 символов — снова появится исключение. При штатной отработке вернётся словарь с проверенными данными. Причём один ключ будет title, а вот второй — text. На это поведение влияет именованный аргумент source.

Если такой объём и формат исходящих/входящих данных вас устраивает, можно оставить один класс. Более развёрнутые примеры классов сериалайзера на запись я приведу в следующей статье.

Создаём экземпляр сериалайзера на запись

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

Аргумент «На чтение» — обработка одной записи из БД или их набора для выдачи по GET-запросу «На запись» — создать новую запись в БД по POST-запросу «На запись» — обновить конкретную запись в БД по PUT- или PATCH-запросу
instance Одна или несколько записей из БД Не передаём Передаём запись из БД, которую собираемся обновить
data Не передаём Словарь с данными, которые хотим валидировать и сохранить в БД. Если many=True, то передаём список словарей Словарь с данными для полного или частичного обновления существующей в БД записи. Если many=True, то передаём список словарей
many Передаём со значением True, если из БД извлекаем не одну, а несколько записей Передаём со значением True, если на вход поступают данные не для одной, а для нескольких будущих записей в БД Передаём со значением True, если хотим частично или полностью обновить сразу несколько записей в БД
partial Не передаём Не передаём Передаём со значением True для PATCH-запросов
context Через этот аргумент можем передать любые данные, которые нужны сериалайзеру

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

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

Валидируем с помощью сериалайзера входные данные

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

Валидацию запускает метод is_valid. Итог его работы ― два новых атрибута сериалайзера: validated_data и errors.

В каждом атрибуте ― словарь, причём один из них всегда пустой. Если ошибок нет, пусто в errors, а если есть ― в validated_data. В первом случае is_valid возвращает True, во втором False.

Рассмотрим, из чего состоят пары «ключ–значение» в этих словарях.

Словарь Ключи Значения
validated_data названия полей сериалайзера значения из поступившего в аргументе data словаря по ключам, имя которых идентично именам полей сериалайзера, а также дефолтные значения полей сериалайзера (если входных данных нет)
errors название полей сериалайзера либо non_field_errors расшифровки ошибок, которые возникли при валидации полей, либо ошибки, возникшей при валидации вне конкретного поля

В поступившем в data словаре по ключу capital_city есть значение ‘London’. Оно успешно валидировано через поле capital_city сериалайзера.

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

Пара значений по ключам capital_city и country не должны повторять идентичное сочетание значений в таблице в БД.

В поступившем на вход словаре по ключу capital_city — пустая строка. Значение не прошло валидацию в поле capital_city сериалайзера, поскольку поле требует непустых значений.

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

У метода is_valid есть один аргумент ― raise_exception. Если у него значение False, которое задано по умолчанию, метод не будет выбрасывать ValidationError. Даже если будут ошибки, метод отработает до конца, вернёт False, а информация об ошибках будет доступна в атрибуте errors. На ошибки любых иных типов настройка raise_exception не влияет.

Как происходит валидация после запуска is_valid

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

Важно: ниже описывается процесс валидации данных, которые предназначены для одной записи в БД. Как и в случае с сериалайзером на чтение, на запись можно выставить many=True и принимать набор данных. Тогда появится ещё одна ступень проверки ― на входе будет ожидаться список словарей, а не один словарь. Далее по этому списку запускается цикл, и каждый отдельный словарь с данными будет проверяться так же, как описано ниже.

Этап 1. Есть ли что валидировать

DRF проверяет, есть ли у сериалайзера атрибут initial_data. Этот атрибут создаётся, если при создании сериалайзера был передан аргумент data. Если его нет, то будет выброшено исключение AssertionError.

Далее идёт проверка содержимого и формата data.

Если в data ничего не оказалось (None), то возможны два исхода:

Если data всё же что-то содержит, DRF проверяет тип поступивших данных — они должны быть словарём.

Этап 2. Проверки на уровне полей

В методе to_internal_value сериалайзер собирает в генератор все поля, которые могут работать на запись, то есть те поля, у которых нет read_only=Truе. Затем сериалайзер перебирает каждое поле в цикле.

Проверка Действие Результат
Есть ли у поля кастомный метод валидации? Для каждого поля внутри класса сериалайзера можно описать проверку значения с любой нужной логикой. Название метода должно быть в формате validate_НазваниеПоля.
Исходный код
Если метод есть, он будет задействован позднее.
Есть ли в поступившем словаре ключ с тем же именем, что и поле сериалайзера? Задача ― извлечь значение для дальнейшей валидации.
Исходный код
Если ключ найден, для дальнейшей валидации берётся его значение, если не найден ― значение empty.

Этап 2.1. Валидирование отсутствующих значений для поля

Если для поля не нашлось одноимённого ключа в поступившем словаре, срабатывает метод validate_empty_values класса fields.Field и проверяет допустимость значения empty.

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

Поле обязательно для заполнения (required=True) Сериалайзер допускает частичное обновление (partial=True) У поля есть дефолтное значение (default=. ) Результат
+ Не имеет значения
(и не может быть, если required=True)
ValidationError
Не имеет значения Поднимается исключение SkipField, поле дальше не валидируется и не попадает в validated_data
+ + Поле дальше не валидируется и не попадает в validated_data
+ Поле валидируется дальше и вместе с дефолтным значением попадает в validated_data

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

В этом случае проверяется, есть ли у поля атрибут allow_null в значении True. Если его нет, появится ValidationError. Если allow_null=True, дальнейшая валидация внутри поля прекратится. Если значение None пройдёт проверку вне поля (метавалидаторами), то это значение и войдёт в validated_data.

После проверок на empty и None запускаются проверочные механизмы внутри конкретного поля.

Этап 2.2. Проверка в поле методом to_internal_value

Важно: если значение empty или None, проверка не проводится.

У каждого поля DRF, которое может работать на запись, есть метод to_internal_value. Чтобы понять логику этого метода, нужно заглянуть под капот в класс соответствующего поля.

Приведу пример ― to_internal_value поля класса CharField.

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

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

Этап 2.3. Проверка поля валидаторами

Важно: проверка не проводится, если значение empty или None.

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

Валидаторы передаются списком в аргументе validators при описании поля сериалайзера, даже если валидатор всего один. Некоторые валидаторы можно передать через специально предусмотренные атрибуты конкретного поля. Например, у поля сериалайзера IntegerField есть аргумент max_value, который создаёт джанго-валидатор MaxValueValidator. Поэтому оба варианта будут верны, но в первом случае нужно ещё сделать импорт из django.core.validators:

Также отмечу, что некоторые поля могут наделяться валидаторами из-под капота без необходимости объявлять их явно. Например, в поле CharField уже заложены два валидатора, названия которых говорят сами за себя: джанго-валидатор ProhibitNullCharactersValidator и DRF-валидатор ProhibitSurrogateCharactersValidator.

Этап 2.4. Проверка кастомным методом validate_названиеПоля

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

Логику задаём любую. Результат работы метода ― возврат значения или ошибки. Скелет метода можно представить так:

Обратите внимание на self — за ним стоит экземпляр сериалайзера. Через него есть доступ к различной ценной информации, которая может пригодиться при валидации. Например, через self.initial_data можно получить доступ ко всему словарю с входными данными до начала их валидации.

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

Этап 2.5. Присвоение имени ключу с успешно валидированным в поле значением

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

Если у поля есть атрибут source, то именем ключа станет не имя соответствующего поля, а значение из атрибута source. Такая логика описана в функции set_values модуля restframework.fields. Эта функция вызывается в конце работы to_internal_value и получает в качестве аргумента keys атрибут source_attrs поля (мы подробно разбирали его в предыдущей статье).

Обратимся к примеру.

Если это поле используется при работе на запись, то сериалайзер будет искать во входных данных ключ content и валидировать значение по этому ключу методом to_internal_value. В случае успеха он вернёт ― внимание! ― валидированное значение уже с ключом ‘text’. Получится ‘text’: ‘валидированное значение, которое пришло с ключом content’. Именно в таком виде пара «ключ–значение» попадут в validated_data, но только если пройдут следующий этап ― проверку метавалидаторами.

Этап 3. Проверка на уровне всего сериалайзера

Этап разбивается на две части.

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

Чтобы задать метавалидатор, нужно прописать внутри класса нашего сериалайзера класс Meta с атрибутом validators. Как и валидаторы на уровне полей, метавалидаторы указывают списком, даже если валидатор один.

Пример метавалидатора из коробки ― UniqueTogetherValidator. Он проверяет, уникально ли сочетание значений из нескольких полей по сравнению с тем, что уже есть в БД.

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

Если в нём есть необходимость, достаточно переопределить метод.

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

Для закрепления: таблица с последовательностью валидирования входных данных в DRF

Этап Что проверяется Метод Примечание
1 Передан ли аргумент data при создании сериалайзера serializers.BaseSerializer.
is_valid
2 Если в data сериалайзеру передано None, допустимо ли это fields.Field.
validate_empty_values
Если передано None, валидация завершается
3 Передан ли в data словарь serializers.Serializer.
to_internal_value

Метод serializers.Serializer.to_internal_value запускает цикл по всем writable-полям со следующими проверками по каждому полю:

Этап Что проверяется Метод Примечание
4 Есть ли в data ключ с таким же именем, что и поле сериалайзера fields.Field.
get_value
Если ключа нет или по нему нет значения и это допустимо, значением поля становится класс empty
5 Если значение поля empty, есть ли у поля дефолтное значение fields.Field.
get_default
Если дефолтного значения нет, поле исключается из валидации. В validated_data оно никак не будет представлено
6 Если значение из входных данных None, допускает ли поле это fields.Field.
validate_empty_values
Допустимость определяется значением атрибута allow_null поля
7 Соответствует ли значение внутренним требованиям поля fields.Field.
классКонкретногоПоля.
to_internal_value
Проверка не проводится, если значение empty или None
8 Проходят ли значение валидаторы, которые встроены в поле или приданы ему fields.Field.run_validators Не предусмотренные изначально валидаторы нужно установить самостоятельно.
Проверка не проводится, если значение empty или None
9 Проходит ли значение поля проверку кастомным методом валидации метод validate_НазваниеПоля класса сериалайзера Логику метода нужно описать самостоятельно

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

Этап Что проверяется Метод Примечание
10 Проходят ли значения полей метавалидаторы атрибут validators класса Meta сериалайзера Для проверки валидаторы нужно задать самостоятельно
11 Проходят ли все значения полей вместе проверку кастомным методом сериалайзера метод validate сериалайзера Для проверки логику метода нужно описать самостоятельно

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

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

Спасибо за внимание к моей статье. Надеюсь, она помогла сделать ещё один шаг в понимании кухни DRF. И снова жду ваших вопросов в комментариях.

Источник

Понравилась статья? Поделить с друзьями:
Добавить комментарий
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Риски бизнеса: без чего не обойтись на пути к успеху
  • django несколько форм на одной странице
  • django несколько моделей в одной форме