Работа с формами¶
О разделе
Этот документ знакомит со способами работы с формами. Для более детальной информации по API форм обращайтесь к API форм, Поля формы и Проверка форм и полей формы.
Если вы планируете создавать сайты и приложения, который принимают и сохраняют данные от пользователей, вам необходимо использовать формы.
Django предоставляет широкий набор инструментов для этого.
HTML формы¶
Форма в HTML – это набор элементов в <form>...</form>, которые позволяют пользователю вводить текст, выбирать опции, изменять объекты и конторолы страницы, и так далее, а потом отправлять эту информацию на сервер.
Некоторые элементы формы - текстовые поля ввода и чекбоксы - достаточно простые и встроены в HTML. Некоторые – довольно сложные, состоят из диалогов выбора даты, слайдеров и других контролов, который обычно используют JavaScript и CSS.
Кроме <input> элементов форма должна содержать еще две вещи:
куда: URL, на который будут отправлены данные
как: HTTP метод, который должна использовать форма для отправки данных
Например, форма авторизации Django содержит несколько <input> элементов: один с type="text" для логина пользователя, один с type="password" для пароля, и один с type="submit" для кнопки «Log in». Она также содержит несколько скрытых текстовых полей, которые Django использует для определения что делать после авторизации.
Форма также говорит браузеру, что данные должны оправляться на URL, указанный в атрибуте action тега <form> - /admin/ - и для отправки необходимо использовать HTTP метод, указанный атрибуте method - post.
При нажатии на элемент <input type="submit" value="Log in"> данные будут отправлены на /admin/.
GET или POST¶
GET и POST – единственные HTTP методы, которые используются для форм.
Форма авторизации в Django использует POST метод. При отправке формы браузер собирает все данные формы, кодирует для отправки, отправляет на сервер и получает ответ.
При GET, в отличии от POST, данные собираются в строку и передаются в URL. URL содержит адрес, куда отправлять данные, и данные для отправки. Пример работы можно посмотреть на странице поиска по документации Django. При оправке формы поиска, вы будете перенаправлены на URL https://docs.djangoproject.com/search/?q=forms&release=1.
GET и POST обычно используются для разных действий.
Любой запрос, который может изменить состояние системы - например, который изменяет данные в базе данных - должен использовать POST. GET должен использоваться для запросов, которые не влияют на состояние системы.
GET также не подходит для формы пароля, поскольку пароль будет отображаться в URL-адресе, а, следовательно, также в истории браузера и журналах сервера, и все это в виде обычного текста. Он также не подходит для больших объемов данных или для двоичных данных, таких как изображения. Веб-приложение, использующее запросы GET для форм администрирования, представляет собой угрозу безопасности: злоумышленнику может быть легко имитировать запрос формы, чтобы получить доступ к конфиденциальным частям системы. POST в сочетании с другими средствами защиты, такими как CSRF-защита Django, обеспечивает больший контроль над доступом.
GET удобен для таких вещей, как форма поиска, т.к. URL, который представляет GET запрос, можно легко сохранить в избранное или отправить по почте.
Роль Django в формах¶
Работа с формами – довольно непростая задача. Возьмем админку Django. Необходимо подготовить для отображения в форме большое количество данных разного типа, отобразить форму в HTML, создать удобный интерфейс для редактирования, получить данные на сервере, проверить и преобразовать в нужный формат, и в конце сохранить или передать для дальнейшей обработки.
Формы Django могут упростить и автоматизировать большую часть этого процесса, и могут сделать это проще и надежнее, чем код, написанный большинством программистов.
Django позволяет:
подготовить данные для отображения в форме
создать HTML формы для данных
получить и обработать отправленные формой данные
Вы можете написать код, который все это будет делать, но Django может выполнить большую часть самостоятельно.
Формы в Django¶
Мы уже описали HTML формы, но HTML <form> только часть необходимого механизма.
В контексте веб-приложения «форма» может относиться к этому HTML <form>, или к Django Form, который ее создает, или к структурированным данным, возвращаемым при их отправке, или к сквозной рабочей коллекции этих частей.
Класс Django Form¶
Сердце всего механизма – класс Form. Как и модель в Django, которая описывает структуру объекта, его поведение и представление, Form описывает форму, как она работает и показывается пользователю.
Как поля модели представляют поля в базе данных, поля формы представляют HTML <input> элементы. (ModelForm отображает поля модели в виде HTML <input> элементов, используя Form. Используется в админке Django.)
Поля формы сами являются классами. Они управляют данными формы и выполняют их проверку при отправке формы. Например, DateField и FileField работают с разными данными и выполняют разные действия с ними.
Поле формы представлено в браузере HTML «виджетом» - компонент интерфейса. Каждый тип поля представлен по умолчанию определенным классом Widget, который можно переопределить при необходимости.
Создание, обработка и рендеринг форм¶
При рендеринге объекта в Django мы обычно:
получаем его в представлении (например, загружаем из базы данных)
передаем в контекст шаблона
представляем в виде HTML в шаблоне, используя переменные контекста
Рендеринг форм происходит аналогичным образом с некоторыми отличиями.
В случае с моделями, вряд ли нам может понадобится пустая модель в шаблоне. Для форм же нормально показывать пустую форму для пользователя.
Экземпляр модели, который используется в представлении, обычно загружается из базы данных. При работе с формой мы обычно создаем экземпляр формы в представлении.
When we instantiate a form, we can opt to leave it empty or pre-populate it, for example with:
сохраненного ранее объекта модели (для редактирования их в админке)
полученные из других источников
получение из предыдущей отправки формы
Последний случай самый интересный, т.к. позволяет пользователям не только читать сайт, но и отправлять ему информацию.
Создание формы¶
Что необходимо сделать¶
Предположим вам необходимо создать простую форму на сайте, которая позволяет пользователю указать имя. Вам необходимо добавить в шаблон следующий код:
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
Этот код указывает браузеру отправить данные на URL /your-name/, используя метод POST. Браузер покажет на странице текстовое поле ввода с меткой «Your name:», и кнопку «OK». Если в контексте шаблона указать переменную current_name, её значение будет добавлено в текстовое поле your_name.
Вам необходимо создать представление, которое рендерит шаблон с HTML формой и передает в контекст шаблона current_name.
При отправке формы будет отправлен POST запрос с данными из формы.
Также необходимо добавить представление, которое обрабатывает запрос на /your-name/ URL.
This is a very simple form. In practice, a form might contain dozens or hundreds of fields, many of which might need to be pre-populated, and we might expect the user to work through the edit-submit cycle several times before concluding the operation.
Вам может понадобится валидация на стороне браузера, или более сложные конролы, например, позволить пользователю выбрать дату из календаря.
На много легче позволить Django сделать эту работу за нас.
Создание форм в Django¶
Класс Form¶
Мы уже знаем какую форму хотим получить в HTML. Для начала нам понадобится следующий класс формы:
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
Этот код создает класс Form с одним полем (your_name). Мы добавили читабельное название поля, которое будет добавлено в тег <label> при рендеринге поля (на самом деле мы могли не добавлять label т.к. аналогичное название Django сгенерировал бы самостоятельно).
Максимальное количество символом в значении мы указали с помощью параметра max_length. Он используется для двух вещей. Будет добавлен атрибут maxlength="100" в HTML тег <input> (теперь браузер не позволит пользователю ввести больше символов, чем мы указали). Также Django выполнит проверку введенного значения, когда получит запрос с браузера с введенными данными.
Экземпляр Form содержит метод is_valid(), который выполняет проверку всех полей формы. Если все данные правильные, это метод:
вернет
Trueдобавит данные формы в атрибут
cleaned_data.
После рендеринга наша форма будет выглядеть следующим образом:
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>
Обратите внимание, она не содержит тег <form>, или кнопку отправки. Вам необходимо самостоятельно их добавить в шаблоне.
Представление¶
Данные формы отправляются обратно в Django и обрабатываются представлением, обычно тем же, которое и создает форму. Это позволяет повторно использовать часть кода.
Для обработки данных формой нам необходимо создать ее в представлении для URL, на который браузер отправляет данные формы:
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
Если в представление пришел GET запрос, будет создана пустая форма и добавлена в контекст шаблона для последующего рендеринга. Это мы и ожидаем получить первый раз открыв страницу с формой.
Если форма отправлена через POST запрос, представление создаст форму с данными из запроса: form = NameForm(request.POST) Это называется «привязать данные к форме» (теперь это связанная с данными форма).
Мы вызываем метод is_valid() формы. Если получили не True, снова рендерим шаблон, передав нашу форму. Теперь форма не пустая (не связана с данными) и HTML форма будет содержать ранее отправленные данные, их можно повторно отредактировать и отправить снова.
Если is_valid() вернет True, мы можем найти проверенные данные в атрибуте cleaned_data. Мы можем сохранить эти данные в базе данных, или выполнить какие-то другие действия над ними, перед тем, как сделать редирект на другую страницу.
Шаблон¶
Наш name.html шаблон может быть довольно простым:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
Все поля формы и их атрибуты будут добавлены в HTML из {{ form }} при рендеринге шаблона.
Формы и CSRF защита
Django поставляется с защитой против Cross Site Request Forgeries. При отправке формы через POST с включенной защитой от CSRF вы должны использовать шаблонный тег csrf_token, как показано в предыдущем примере. Тем не менее, раз CSRF-защита не обязательна для применения в шаблонах при оформлении формы, этот тег опущен в последующих примерах.
HTML5 поля и проверка в браузере
Если ваша форма включает URLField, EmailField или любой целочисленный тип поля, Django будет использовать типы ввода HTML5 url, email и number. По умолчанию браузеры могут применять к этим полям собственную проверку, которая может быть более строгой, чем проверка Django. Если вы хотите отключить это поведение, установите атрибут novalidate в теге form или укажите в поле другой виджет, например TextInput.
Теперь у нас есть полностью рабочая форма, созданная классом Form, обрабатываемая представлением, и представленная на странице как HTML <form>.
Это все что вам нужно для начала, но формы в Django предоставляют на много больше возможностей. Как только вы разберетесь со всем, что описано выше, можете приступать к изучению всех возможностей, которые предоставляет Django.
Дополнительные возможности Django Form¶
Все классы форм создаются как подклассы либо django.forms.Form, либо django.forms.ModelForm. Вы можете думать о ModelForm как о подклассе Form. Form и ModelForm фактически наследуют общую функциональность от (частного) класса BaseForm``, но эти детали реализации редко бывают важны.
Модели и формы
Если ваша форма будет использоваться для создания или редактирования объекта модели, вы можете использовать ModelForm. Она поможет сэкономить много времени и кода, т.к. позволяет создать форму по классу модели.
Связанные и не связанные с данными экземпляры формы¶
Важно понимать разницу между Заполненные и незаполненные формы:
Незаполненная форма не содержит данных, привязанных к её полям. При отображении формы пользователь увидит её пустой или содержащей значения по умолчанию.
Заполненная форма содержит переданную информацию и, следовательно, может быть использована для проверки введённых данных. При отображении заполненной формы, не прошедшей проверку, она будет содержать встроенные сообщения об ошибках, которые расскажут пользователю о причинах отказа в принятии его данных.
Атрибут формы is_bound позволяет узнать связана форма с данными или нет.
Поля формы¶
Рассмотрим более полезную форму, чем наш минимальный пример выше, которая может быть использована для реализации функционала «свяжитесь с нами» на личном веб сайте:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
Наша первая форма содержала только одно поле your_name типа CharField. В этом случае, форма содержит четыре поля: subject, message, sender и cc_myself. CharField, EmailField и BooleanField – это просто три стандартных типа поля; полный список полей может быть найден в Поля формы.
Виджеты¶
Каждое поле формы содержит соответствующий класс Widget, который отвечает за создание HTML кода, представляющего поле, например <input type="text">.
В большинстве случаев поле уже содержит подходящий виджет. Например, по умолчанию поле CharField представлено виджетом TextInput, который создает тег <input type="text"> в HTML. Если вам вместо него необходим <textarea>, вы можете указать подходящий виджет при определении поля формы, как мы сделали для поля message.
Данные поля¶
Когда в форму добавлены данные, и она проверена методом is_valid() (и is_valid() вернул True), проверенные данные будут добавлены в словарь form.cleaned_data. Эти данные будет преобразованы в подходящий тип Python.
Примечание
Также вы можете напрямую обратиться к непроверенным данным через request.POST, но лучше использовать проверенные данные.
В приведённом ранее примере, cc_myself будет булевым значением. Аналогично, такие поля как IntegerField и FloatField преобразовывают свои значения в типы Python int и float соответственно.
Вот небольшой пример того, как можно обрабатывать данные в представлении, к которому привязана форма:
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['info@example.com']
if cc_myself:
recipients.append(sender)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
Совет
Подробную информацию про отправку электронной почты с помощью Django можно найти в Отправка электронных писем.
Некоторые поля требуют дополнительной обработки. Например, загруженные файлы необходимо обрабатывать по другому (их можно получить из request.FILES, а не request.POST). Подробности смотрите в Привязка загруженных файлов к форме.
Работа с шаблонами формы¶
Чтобы получить доступ к форме в шаблоне, просто передайте экземпляр в контекст шаблона. Если ваша форма добавлена в контекст как form, {{ form }} создаст необходимые теги <label> и <input>.
Form rendering options¶
Что еще необходимо при рендеринге формы
Обратите внимание, форма не добавляет тег <form> и submit кнопку. Вы должны добавить их самостоятельно.
There are other output options though for the <label>/<input> pairs:
{{ form.as_table }}will render them as table cells wrapped in<tr>tags{{ form.as_p }}will render them wrapped in<p>tags{{ form.as_ul }}will render them wrapped in<li>tags
Note that you’ll have to provide the surrounding <table> or <ul>
elements yourself.
Here’s the output of {{ form.as_p }} for our ContactForm instance:
<p><label for="id_subject">Subject:</label>
<input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label>
<textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
<input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
Note that each form field has an ID attribute set to id_<field-name>, which
is referenced by the accompanying label tag. This is important in ensuring that
forms are accessible to assistive technology such as screen reader software.
You can also customize the way in which labels and ids are generated.
See Выдача формы в виде HTML for more on this.
Рендеринг полей вручную¶
We don’t have to let Django unpack the form’s fields; we can do it manually if
we like (allowing us to reorder the fields, for example). Each field is
available as an attribute of the form using {{ form.name_of_field }}, and
in a Django template, will be rendered appropriately. For example:
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
Элемент <label> также может быть создан с помощью метода label_tag(). Например:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
Рендеринг ошибок проверки¶
Цена этой гибкости — немного больше работы. До сих пор нам не приходилось беспокоиться о том, как отображать ошибки формы, потому что об этом позаботились за нас. В этом примере нам нужно было убедиться, что мы позаботились о любых ошибках для каждого поля и любых ошибках для формы в целом. Обратите внимание {{ form.non_field_errors }} в верхней части формы и шаблон поиска ошибок в каждом поле.
Список ошибок можно вывести используя {{ form.name_of_field.errors }}. Они будут выглядеть приблизительно как:
<ul class="errorlist">
<li>Sender is required.</li>
</ul>
Списку назначен CSS-класс errorlist, что позволяет вам настроить параметры его отображения. Если потребуется более тонкая настройка отображения ошибок, вы можете это организовать с помощью цикла по ним:
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
Ошибки, не относящиеся к полям, (и/или ошибки скрытых полей, которые отображаются наверху формы при использовании методов подобных form.as_p()) будут отображаться с дополнительным классом nonfield, что поможет их отделить от ошибок полей формы. Например, {{ form.non_field_errors }} может выглядеть так:
<ul class="errorlist nonfield">
<li>Generic validation error</li>
</ul>
Подробности о выводе ошибок, стилях и атрибутах смотрите в API форм.
Цикл по полям формы¶
Если вы используете однотипный HTML для каждого поля формы, вы можете избежать дублирования кода, используя тег {% for %} для прохода по полям формы:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
Полезные атрибуты {{ field }}:
- {{ field.label }}
Метка поля, т.е.
Email address.- {{ field.label_tag }}
The field’s label wrapped in the appropriate HTML
<label>tag. This includes the form’slabel_suffix. For example, the defaultlabel_suffixis a colon:<label for="id_email">Email address:</label>
- {{ field.id_for_label }}
ID, которое будет использоваться для этого поля (
id_emailв примере выше). Вы можете использовать его вместоlabel_tag, если самостоятельно генерируете <label> для поля. Так полезно при генерации JavaScript, если вы не хотите «хардкодить» ID.- {{ field.value }}
Значение поля, например
someone@example.com.- {{ field.html_name }}
Имя поля, которое будет использовано в HTML-поле. Здесь учитывается префикс формы, если он был установлен.
- {{ field.help_text }}
Любой вспомогательный текст, который привязан к полю.
- {{ field.errors }}
Вывод
<ul class="errorlist">, содержащий все ошибки валидации, относящиеся к полю. Вы можете настраивать представление списка ошибок с помощью цикла{% for error in field.errors %}. В этом случае, каждый объект в цикле является простой строкой, содержащей сообщение об ошибке.- {{ field.is_hidden }}
Значение этого атрибута равно
True, если поле является скрытым, иFalseв противном случае. Данный атрибут обычно не используется при выводе формы, но может быть полезен в условиях подобных этому:
{% if field.is_hidden %}
{# Do something special #}
{% endif %}
- {{ field.field }}
Экземпляр
Fieldиз класса формы, который обёрнут с помощьюBoundField. Он предоставляет доступ к атрибутамField, например{{ char_field.field.max_length }}.
См.также
Полный список методов и атрибутов смотрите в описании BoundField.
Повторное использование шаблонов форм¶
If your site uses the same rendering logic for forms in multiple places, you
can reduce duplication by saving the form’s loop in a standalone template and
using the include tag to reuse it in other templates:
# In your form template:
{% include "form_snippet.html" %}
# In form_snippet.html:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
If the form object passed to a template has a different name within the
context, you can alias it using the with argument of the include
tag:
{% include "form_snippet.html" with form=comment_form %}
If you find yourself doing this often, you might consider creating a custom inclusion tag.
Изучите далее¶
Мы рассмотрели базовые возможности форм, но они могут больше:
- Наборы форм
BaseFormSet- Использование начальных данных с наборами форм
- Ограничение максимального количества форм
- Проверка набора форм
- Проверка количества форм
- Сортировка и удаление форм
- Добавление дополнительных полей к набору форм
- Как передать свои параметры в формы набора форм
- Настройка префикса набора форм
- Использование наборов форм в представлениях и шаблонах
- Создание форм из моделей
- Статические файлы форм (класс
Media)
См.также
- Справочник по формам
Предоставляет полный справочник на API, включая поля, виджеты и валидацию как полей, так и формы.