django форма выпадающий список

Поле множественного выбора с автодополнением в Django

Привет, хабр.
В прошлой своей статье я описал технологию создания кастомного поля для ввода тегов в Django. Сейчас я бы хотел поделиться готовым и более-менее универсальным решением, реализующим поле множественного выбора с автодополнением по AJAX. Отличие этого поля от описанного в предыдущей статье в том, что оно позволяет только выбирать элементы из справочника, но не создавать новые. За front-end часть будет отвечать замечательный jQuery-плагин Select2. Решение будет оформлено в виде отдельного приложения Django.

Виджет

Конструктор виджета принимает класс модели, флаги multiple и disabled, отвечающие, соответственно, за множественность выбора и активность поля. В субклассе Media подключаются скрипты и стили Select2. Скрипт, инициализирующий плагин Select2, будет описан в шаблоне infinite_choice_widget.html.

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

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

Функция django.db.models.get_model фозвращает класс модели. Далее, в зависимости от переменных запроса, из модели выбираются либо варианты, начинающиеся со строки term, либо варианты, имеющие id равные переданным в переменной ids. Второй случай происходит при инициализации плагина с уже введенными ранее данными.
Я добавил в response заголовок Cache-Control, с временем жизни данных в кэше — сутки. Это чтоб не дергать базу однотипными запросами. Очень помогает при использовании поля с огромными справочниками типа КЛАДР/ФИАС.

А так выглядит запись в urls.py для нашей вьюхи.

Поле формы

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

Я не стал наследоваться от ModelMultipleChoiceField, так как оно работает с queryset, а нам надо работать с моделью.
Конструктор инициализирует виджет переданными моделью, флагами multiple и disabled и специфичными аттрибутами.
Метод to_python получает в качестве value либо одиночный id, либо несколько id в одной строке через запятую и обрабатывает его в зависимости от флага multiple. В обоих случаях проверяется наличие выбранных id в модели.
Метод prepare_value подготавливает инициализирующие данные для отображения: если в параметре initial поля передан одиночный инстанс модели, то возвращает id этого инстанса в виде строки; если же передан список инстансов или QuerySet, то возвращает строку с id через запятую.

Заключение

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

, где ChoiceModel — произвольная модель вида

Осталось не забыть подключить приложение в settings.py,

и вывести статику формы TestForm в шаблоне

Источник

Имеется такой список объектов, отображенных по значению атрибутов товаров. sM6vgwg

TEMPLATES

Search_Indexes.py

1 ответ 1

В самом тэге нужно указать форму.

И еще 1 очень важный момент, если речь идет о Django(oscar), не знаю как у кого где описано, но в Oscar есть специальный ui.js, в которым описаны функции которые передают значения из поля, когда пользователь нажал на форму выбора, выглядит это так:

Далее в Template следует передать значения от словаря.

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

Далее следует итрировать объекты полученные из словаря в choice_tags, и добавить их в массив(ну либо в словарь или вообще сразу передавать в форму). Делается это как угодно я лично сделал так.

Ну и передаем items,field в форму AddToChoiceForm

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

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

Источник

Руководство часть 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. Выпадающий список в форме

Имеется модель Post с полем «category». В модели есть несколько объектов с разными значениями этого поля (‘url’, ‘file’, ‘video’). Как при создании объекта через форму сделать выпадающий список для этого поля, чтобы можно было либо выбрать одно значение из существующих (отсеив дубликаты), либо вписать новое значение? Пробую сделать через

но в выпадающем списке вываливается всё в виде словарей:

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

1 ответ 1

Список уникальных значений можно получить так:

Но, если нужно уметь редактировать эти категории, то лучше сделать отдельную модель Category и привязать её к Post как ForeignKey

r8X5i

Всё ещё ищете ответ? Посмотрите другие вопросы с метками django или задайте свой вопрос.

Похожие

Подписаться на ленту

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

дизайн сайта / логотип © 2021 Stack Exchange Inc; материалы пользователей предоставляются на условиях лицензии cc by-sa. rev 2021.11.5.40661

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Источник

Глава 5 «Формы» из книги Pro Django

Вольный перевод главы 5 «Формы» из книги Marty Alchin. Pro Django. Apress, 2013, выполнен django43ru@gmail.com

Глава 5. Формы.

Введение.

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

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

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

Объявление и определение полей формы.

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

Но между полями модели и полями формы Django есть и отличия. Первое – это то, как класс формы и класс модели распознают свои поля. Класс модели, вообще-то говоря, не распознает тип полей модели вовсе, он просто проверяет, имеет ли поле метод contribute_to_class () и вызывает его, когда происходит обращение к этому полю. А вот формы Django проверяют тип каждого поля класса, чтобы выяснить, имеет ли поле тип django.forms.fields.Field.

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

Например, форма для обратной связи (ContactForm) может принимать объект типа User для того, чтобы определить, авторизован ли текущий пользователь, или нет. Если пользователь не авторизован, то в форму следует добавить поле для ввода имени пользователя:

Привязка данных к форме.

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

Обратите внимание на то, что все значения в словаре имеют тип string. Некоторые поля могут принимать значения и других типов, например, integer, но тип string является стандартным передаваем типом значения и все типы полей знают, как его обрабатывать. Это необходимо для поддержки наиболее распространенного способа создания экземпляра формы внутри какого-либо представления, с использованием словаря request.POST, доступного в представлении.

Иногда форма может принимать файлы, которые предоставляются иначе, нежели другие типы данных, вводимых пользователем. Файлы могут быть доступны как атрибут FILES входящего объекта запроса request. Если ваша форма имеет в себе поля типа FileField, то при ее создании вторым позиционным аргументом в конструкторе должен быть атрибут request.FILES.

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

Проверка введенных данных.

После того, как форма была связана с данными, данные могут быть проверены. Всегда рекомендуется проверять данные, и никогда не полагаться на пользователя. Проверка данных необходима как для предупреждения сохранения ошибочных данных, так и для предотвращения возможных атак на сайт. Проверить данные формы очень просто, для этого достаточно вызвать метод формы is_valid(), который возвращает True, если данные прошли проверку, и False – в противном случае.

НИКОГДА НЕ ДОВЕРЯЙТЕ ПОЛЬЗОВАТЕЛЮ!

Разумеется, большинство пользователей Интернета являются добропорядочными пользователями и не желают вам зла, однако встречаются и злоумышленники, которые намеренно пытаются вывести из строя ваш сайт. Во избежание проблем, просто всегда исходите из того, что данные в ваши формы вводятся ошибочные и злонамеренные, это позволит вам предотвратить многие проблемы с безопасностью. Например, пользователь не должен иметь возможность сохранять свои данные в области, предназначенные только для чтения. Он не должен иметь прямого доступа к БД и т.п. Всего можно выделить четыре основных типа атак, которые злоумышленник может предпринять, передавая некорректные данные: SQL injection, Cross-site Scripting, Cross-site Request Forgery, Form Manipulation. Для предупреждения этих атак обязательно используйте метод формы is_valid(), и никогда не полагайтесь на проверку введенных данных средствами javascript в браузере пользователя. Дело в том, что по пути от браузера до сервера у злоумышленников достаточно возможностей для внесения искажений в данные пользователя.

При вызове метода is_valid() происходит, среди прочего, вызов метода clean() для каждого поля формы по отдельности. Именно этот метод clean() и решает, является ли введенное значение корректным для этого поля, и в случае ввода некорректных данных возвращает описание соответствующий ошибки. В большинстве случаев такой проверки достаточно, однако иногда требуется «расширенная» проверка, и Django предоставляет возможность сделать такую проверку. Для реализации дополнительной собственной проверки корректности введенного пользователем значения для поля формы, создайте соответствующий метод в классе формы. Имя метода должно выглядеть так: cleaned_[имя поля формы](), где [имя поля формы] – ваше поле, значение которого вы и хотите проверить. Например, для поля c именем title этот метод должен называться cleaned_title(). Этот метод будет искать значение для соответствующего поля в словаре cleaned_data, проверяя его на соответствие своим требованиям. Если возникает необходимость в дополнительной «очистке» значения, то этот метод должен, после проведения соответствующей дополнительной «очистки», переписать значение поля в словарь cleaned_data.

Использование представлений, основанных на классах.

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

Как было показано ранее в Главе 4, это может быть сделано более контролируемым способом, путем обработки GET и POST запросов раздельно, в представлении, основанном на классе.

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

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

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

Есть несколько методов класса FormViews, которые вы можете переопределить для пущего контроля над ситуацией.

get_form_class(self) — Возвращает класс формы. По умолчанию, просто возвращает атрибут form_class, который имеет значение None, если вы не предоставляете что-то свое

get_initial(self) — Возвращает словарь, передаваемый в конструктор формы. По умолчанию – пустой словарь.

get_form_kwargs(self) — Возвращает словарь для использования в качестве ключевых аргументов при создании экземпляра формы для каждого запроса. По умолчанию, он включает в себя результат вызова метода get_initial(), и, если запрос был типа POST или PUT, он также добавляет request.POST и Request.Files.

get_form(self, form_class) — Возвращает полностью определенный экземпляр формы, имеющий класс, возвращаемый методом get_form_class(), который получает при создании аргументы, полученные из метода get_form_kwargs(). Учитывая то, что вы имеете полный контроль над всеми аргументами, передаваемыми из метода get_form_kwargs(), вызов этого метода имеет смысл использовать только для того, чтобы внести изменения в форму после того, как она была создана, но еще не была проверена.

form_valid(self, form) — Этот метод – основная «рабочая лошадка», который вызывается после проверки формы, если проверка прошла успешно. В нем вы можете производить собственную дополнительную проверку формы. По умолчанию, он перенаправляет пользователя по адресу, возвращаемому методом get_success_url().

form_invalid(self, form) — Аналог form_valid(), вот только вызывается он тогда, когда форма не прошла валидацию. По умолчанию, просто перерисовывает шаблон формы.

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

При работе с формой для какой-то конкретной модели используйте класс ModelForm, возможности которого еще больше. А в модуле django.views.generic.eidt есть и другие обобщенные представления, которые позволяют редактировать данные соответствующей модели:

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

Представление DeleteView здесь стоит несколько особняком. Для его нормальной работы, кроме модели, необходимо указать success_url – путь, по которому будет перенаправлен пользователь после успешного удаления объекта соответствующей модели. Представления CreateView и UpdateView связаны с существующим объектом, поэтому реализация по умолчанию переадресует, после обработки запроса, по адресу, возвращаемому методом get_absolute_url() соответствующего объекта. В случае же с DeleteView, удаляемый объект после удаления больше не существует, и поэтому нельзя вызвать его метод get_absolute_url().

Собственные поля.

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

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

Как уже говорилось выше, все поля формы – в том числе, разумеется, и собственные поля – обязаны наследоваться от класса django.forms.fields.Field. Это позволяет форме обрабатывать свои поля должным образом. Класс поля формы определяет несколько атрибутов и методов, определяющих его поведение. Например, можно указать, какой виджет следует использовать для ввода данных в это поле, или задать описания ошибок, возникающих при «очистке» данных и т.п. Вы можете полностью определить поведение собственного поля самостоятельно.

Проверка.

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

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

Это означает, что сообщения об ошибках могут поступать из трех источников: из самого класса поля, из его родительского класса, и из аргументов, передаваемых в конструктор класса поля при его создании. При возникновении исключения, в методе clean() должен быть простой способ получения сообщения об ошибке, откуда бы оно – сообщение об ошибке – не бралось бы. Для обеспечения этой простоты, Django заполняет атрибут error_messages для каждого экземпляра поля, который содержит все сообщения об ошибках, которые были заданы всеми тремя вышеуказанными способами. Таким образом, в методе clean() просто нужно будет взять описание для возникающей ошибки из словаря self.error_measages по соответствующему ключу, и использовать это значение – описание ошибки – в качестве аргумента для ValidationError.

Обратите внимание, что здесь используется метод super() для вызова метода clean() родительского класса, который проверяет, что значение value является допустимыми decimal-числом. А уже потом – если проверка на принадлежность типу decimal прошла успешно – осуществляется проверка на то, что введенное значение может быть широтой или долготой.

Работа с виджетами.

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

Второй атрибут – hidden_widget – определяет виджет, который должен быть использован в тех случаях, для поля должна быть HTML-разметка, но пользователь при этом не должен видеть ее. Не переопределяйте этот атрибут без необходимости, ибо его значение по умолчанию – HiddenInput – достаточно в большинстве случаев. Для некоторых полей, требующих указания нескольких значений, подобных, например, MultipleChoiceField, используется MultipleHiddenInput.

Дополнительно вы можете определить метод widget_attrs(self, widget_object), который позволяет добавить html-атрибуты к инициализированному экземпляру виджета. Вместо присоединения к инициализированному виджету html-атрибутов напрямую, лучше верните из метода widget_attrs() словарь нужных вам html-атрибутов. Такая техника присоединения атрибутов к виджету использована в Django, например, для добавления к полю CharField атрибута html-разметки maxlength.

Определение поведения HTML-разметки.

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

Собственные виджеты.

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

Желательно, чтобы ваш виджет был наследником от класса django.forms.widgets.Widget, хотя это и не обязательно. Указанный класс предоставляет базовую функциональность для виджета, и вы можете переопределить в своем виджете только нужные вам атрибуты и методы.

Генерация HTML-разметки виджета.

Основной задачей виджета является предоставление интерфейса пользователю для ввода значения. Эта задача решается путем генерации виджетом соответствующей html-разметки.

Рассмотрим пример. У нас есть поле, значение которого задано в процентах. Вы захотели к полю ввода на странице добавить значок процентов «%». Для решения этой задачи вам необходимо определить метод render(self, name, value, attrs=None) вашего виджета. В качестве аргументов в метод передаются следующие аргументы: self – говорящий о том, что эта функция является методом, name – название элемента, которое он будет иметь в html-коде, value – значение, которое должно быть отображено в поле ввода, attrs – словарь html-атрибутов для тонкой настройки вашего виджета. Вот как это реализуется в коде:

Получение данных, отправленных POST-запросом.

Виджеты не только обеспечивают непосредственный ввод данных на HTML-странице, но и сопоставляют введенные данные полям формы. Их использование позволяет скрыть от полей работу с HTML-кодом, а также позволяет управлять множественным HTML –вводом данных, и заполнять поля формы на странице «значениями по умолчанию».

Метод виджета, ответственный за выполнение этой задачи – value_from_datadict(data, files, name), где:

data – словарь для конструктора формы, обычно это – request.POST

files – файлы, передаваемые конструктору формы, имеющих формат, схожий с request.FILES

name – имя виджета, которое формируется как имя поля + любой префикс, что был добавлен на форму

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

Разделение значения поля по нескольким виджетам.

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

Детали реализации зависят от того, какие html-элементы вы собираетесь для этого использовать, но общая идея достаточно простая. Поле передает свое значение виджету, который решает, как отобразить полученное значение в HTML-разметке. Отображение в HTML происходит в методе виджета render(), и именно посредством этого метода виджет может «раскидать» полученное от поля значение по нескольким HTML-элементам. Например, значение с типом DateTimeField может быть «раскидано» по input-элементам для ввода даты и времени раздельно друг от друга.

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

К несчастью, все это требуется от каждого виджета: ответственность за HTML-разметку и сборка полученных данных в одно значение. Иногда бывает полезно скомбинировать два или более уже существующих поля, используя их готовые виджеты, вместо того, чтобы реализовывать всю работу виджета своими силами. Django предоставляет для этого некоторые возможности. Если быть точным, Django предоставляет для этой цели две утилиты: поле MultiValueField, а также виджет MultiWidget, которые разработаны для совместного использования. По отдельности друг от друга они, в общем-то, не очень полезны. Вместе они обеспечивают значительные возможности, которые могут быть кастомизированы (настроены) в классах-потомках.

Если смотреть со стороны поля, то MultiValueField заботится об «очистке» данных, проверяя каждое входящее в него поле по-отдельности. MultiValueField предоставляет своим потомкам только две возможности для настройки: первое, это то, какие поля, составляющие его, должны быть объединены, и второе, это то, каким образом из полей, составляющих его, должно быть получено значение соответствующего python-типа. Например, в Django, поле типа SplitDateTimeField получается путем комбинирования полей типа DateField и TimeField, которые, взятые вместе, приводятся к значению типа dateime.

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

«Сборка» нескольких значений полей в одно происходит в методе compress(), который, помимо self, принимает еще один аргумент – последовательность значений полей, которые должны быть скомбинированы в некое единое значение, имеющее python-тип. Внутри метода сам процесс объединения может выглядеть достаточно сложно. Здесь нужно иметь в виду несколько моментов. Во-первых, на вход метода может поступить пустая последовательность, либо последовательность, имеющая какой-либо пустой элемент, из-за того, например, что пользователь просто не ввел нужное значение. По умолчанию, все поля обязательны к заполнению, и поэтому в этом случае будет сгенерировано исключение еще до вызова метода compress(). Если же поле было объявлено как необязательное к вводу, т.е. имеет required=False, то метод compress() должен возвращать None, и никакого исключения генерироваться не должно.

Кроме того, вполне может оказаться, что будет получена лишь часть из набора значений, поскольку все эти значения вводятся через html-страницу независимо друг от друга. Опять же, если поле является обязательным для заполнения, т.е. если оно имеет required=True (значение по умолчанию), то эта ситуация обрабатывается автоматически, как было сказано выше. Но если поле является необязательным, то метод compress() должен проделать дополнительную работу для того, чтобы обеспечить получение конечного результата для этого неполного набора данных. Обычно это достигается путем последовательной проверки каждого элемента входной последовательности на соответствие каждому из элементов кортежа django.forms.fields.EMPTY_VALUE. Любая часть составного поля, оказавшаяся незаполненной, должна сгенерировать исключение, сообщающее об отсутствующем значении.

Противоположным по назначению методу compress() является метод decompress(). Он принимает в качестве аргумента исходное значение поля, имеющее необходимый python-тип, и разбивает его на составные части, возвращая последовательность значений для составляющих поля в виде кортежа или списка. Поскольку в нашем случае LatLonField уже возвращает кортеж, то нам нужно лишь предусмотреть случай, когда данные не были введены и следует вернуть кортеж из пустых данных:

Настройка разметки формы.

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

Источник

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