Работа с формами

About this document

Этот документ знакомит со способами работы с формами. Для более детальной информации по API форм обращайтесь к API форм, Поля формы и Проверка форм и полей формы.

django.forms – библиотека для работы с формами.

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

  1. Автоматически генерировать HTML код формы с использованием необходимых виджетов.
  2. Проверять переданную информацию с помощью набора правил валидации.
  3. Заново отображать форму при обнаружении ошибок.
  4. Преобразовывать переданную информацию в соответствующие типы данных языка Python.

Введение

Библиотека работает с нижеперечисленными сущностями:

Widget
Класс, который соответствует определённому HTML коду т.е. <input type="text"> или <textarea>. Он реализует генерацию HTML кода для виджета.
Field
Класс, который отвечает за проведение валидации, т.е. EmailField обеспечивает получение правильного адреса электронной почты.
Form
Коллекция полей, которые знаю как проверять введённую информацию и отображать себя в виде HTML кода.
Form Media
CSS и JavaScript ресурсы, которые необходимы для правильного отображения формы.

Эта библиотека отделена от остальных компонентов Django, таких как слой взаимодействия с базой данных, представления и шаблоны. Оно зависит только от настроек Django, ряда вспомогательных функций из django.utils и механизма интернационализации (необязательная зависимость).

Объекты форм

Объект формы содержит в себе последовательность полей формы и коллекцию правил валидации, которые должны быть пройдены для того, чтобы форма приняла переданную информацию. Классы форм создаются как подклассы django.forms.Form и используют декларативный стиль, аналогичный используемому в моделях Django.

Например, рассмотрим форму созданную для реализации функционала “свяжитесь со мной” на личном веб сайте:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

Форма составлена из объектов Field objects. В нашем случае, форма содержит четыре поля: subject, message, sender и cc_myself. CharField, EmailField и BooleanField – это просто три стандартных типа поля; полный список полей может быть найден в Поля формы.

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

Использование формы в представлении

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

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

В этом коде заложено три сценария:

  1. Если форме не передана информация, то создаётся чистый экземпляр ContactForm и передаётся в шаблон.
  2. Если форма получила переданную информацию, создаётся заполненный из request.POST экземпляр формы. Если переданная информация прошла проверку, то она обрабатывается и пользователь перенаправляется на страницу с благодарностью за ввод информации.
  3. Если форма была заполнена, но переданная информация не прошла проверку, то заполненный экземпляр формы отправляется в шаблон.

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

Обратитесь к Заполненные и незаполненные формы для подробной информации по различиям между заполненной и незаполненной формами.

Обработка закачки файла через форму

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

Обработка данных формы

После того, как is_valid() возвратила True, вы можете спокойно работать с полученными данными, зная, что они прошли валидацию в вашей форме. Несмотря на наличие прямого доступа к request.POST, рекомендуется использовать form.cleaned_data. Данные, представленные этой структурой не только проверены, но и преобразованы в соответствующие типы языка Python. В приведённом выше примере, cc_myself представлено булевым значением. Аналогично, поля, подобные IntegerField и FloatField преобразуют значения в типы int и float соответственно. Следует отметить, что нередактируемые поля (readonly) не представлены form.cleaned_data (и установка значения в перекрытом методе clean() не будет иметь эффекта), потому что эти поля отображаются как текст, а не элемент ввода и, таким образом, не отправляются обратно на сервер.

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

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)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST

Подробную информацию про отправку электронной почты с помощью Django можно найти в Sending email.

Отображение формы с помощью шаблона

Формы разработаны для работы с шаблонным языком Django. В приведённом ранее примере мы передавали экземпляр ContactForm в шаблон, используя контекстную переменную form. Покажем простой пример шаблона:

<form action="/contact/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

Форма просто выводит свои поля. Указание тага <form> и кнопки для отправки формы – это ваша задача.

Forms and Cross Site Request Forgery protection

Django поставляется с защитой против Cross Site Request Forgeries. Для отправки формы через POST при включенной защите от CSRF вы должны использовать шаблонный таг csrf_token, как показано в предыдущем примере. Тем не менее, раз CSRF защита не обязательна для применения в шаблонах при оформлении формы, этот таг опущен в последующих примерах.

Таг form.as_p выводит поля формы в виде параграфов (т.е. <p/>) вместе с соответствующими метками. Ниже представлены пример с результатом использования нашего шаблона:

<form action="/contact/" method="post">
<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label>
    <input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
    <input type="text" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
</form>

Следует отметить, что каждое поле формы обладает атрибутом с идентификатором id_<field-name>, с помощью которого обеспечивается связь с тагом метки. Это позволяет формам быть дружественными к вспомогательным технологиям, например, это поможет работе ПО для слепых. Также вы можете настроить способ генерации меток и идентификаторов.

Вы можете использовать form.as_table для вывода полей формы в виде таблицы (потребуется прописать в шаблоне таги <table>) и form.as_ul для вывода полей формы в виде элементов списка.

Настройка шаблона формы

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

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="id_message">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="id_sender">Your email address:</label>
        {{ form.sender }}
    </div>
    <div class="fieldWrapper">
        {{ form.cc_myself.errors }}
        <label for="id_cc_myself">CC yourself?</label>
        {{ form.cc_myself }}
    </div>
    <p><input type="submit" value="Send message" /></p>
</form>

Каждое именованное поле формы может быть выведено в шаблоне с помощью тага {{ form.name_of_field }}, который создаст HTML код необходимый для отображения виджета. Использование тага {{ 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 %}

Цикл по полям формы

Если вы используете однотипный HTML для каждого поля формы, вы можете избежать дублирования кода, используя таг {% for %} для прохода по полям формы:

<form action="/contact/" method="post">
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>

Внутри цикла таг {{ field }} представляет собой экземпляр класса BoundField. Класс BoundField также имеет приведённые далее атрибуты, которые могут быть полезны в вашем шаблоне:

{{ field.label }}
Метка поля, т.е. Email address.
{{ field.label_tag }}
Метка поля обёрнута в соответствующий HTML таг <label>, т.е. <label for="id_email">Email address</label>.
{{ 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 %}

Цикл по скрытым и отображаемым полям

Если вы вручную размещаете форму в шаблоне, то у вас появляется возможность трактовать <input type="hidden"> поля по своему. Например, так как скрытые поля не отображаются на форме, размещение сообщений об ошибке для поля “перейти далее” может смутить пользователей. Такие ошибки следует обрабатывать другим способом.

Django предоставляет два метода класса формы, которые позволяют организовать раздельные циклы по скрытым и отображаемым полям: hidden_fields() и visible_fields(). Покажем как изменится наш пример, если воспользоваться этими методами:

<form action="/contact/" method="post">
    {# Include the hidden fields #}
    {% for hidden in form.hidden_fields %}
    {{ hidden }}
    {% endfor %}
    {# Include the visible fields #}
    {% for field in form.visible_fields %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>

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

Повторное использование шаблонов форм

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

<form action="/contact/" method="post">
    {% include "form_snippet.html" %}
    <p><input type="submit" value="Send message" /></p>
</form>

# In form_snippet.html:

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }}: {{ field }}
    </div>
{% endfor %}

Если объект формы, переданный в шаблон, имеет другое имя в контексте, вы можете создать для него псевдоним, используя аргумент with тага include:

<form action="/comments/add/" method="post">
    {% include "form_snippet.html" with form=comment_form %}
    <p><input type="submit" value="Submit comment" /></p>
</form>

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