API форм

Об этом документе

Этот документ знакомит с деталями API форм Django. Сначала вы должны прочитать введение в использование форм.

Заполненные и незаполненные формы

Экземпляр класса Form может быть заполнен набором данных или не заполнен.

  • Если он заполнен, то у него есть возможность валидации полученных данных и генерации заполненной формы в виде HTML кода.
  • Если он не заполнен, то выполнить валидацию невозможно (так как нет для этого данных!), но есть возможность сгенерировать HTML код пустой формы.
class Form

To create an unbound Form instance, instantiate the class:

>>> f = ContactForm()

Для привязки данных к форме, передайте данные в виде словаря в качестве первого параметра конструктора класса Form:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)

В этом словаре, ключами являются имена полей, которые соответствуют атрибутам в вашем классе Form. Значения словаря являются данными, которые вам требуется проверить. Обычно значения представлены строками, но это требование не является обязательным. Тип переданных данных зависит от класса Field, это мы сейчас рассмотрим.

Form.is_bound

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

>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True

Следует отметить, что передача форме пустого словаря создаёт заполненную форму без данных:

>>> f = ContactForm({})
>>> f.is_bound
True

Если есть заполненный экземпляр Form и требуется как-то изменить его данные или если требуется привязать данные к незаполненному экземпляру Form, то создаёте другой экземпляр Form. Нет способа изменить данные в экземпляре Form. После создания экземпляра Form, его данные следует рассматривать как неизменные, независимо от его заполненности.

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

Form.clean()

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

Form.is_valid()

Главной задачей объекта Form является проверка данных. У заполненного экземпляра Form вызовите метод is_valid() для выполнения проверки и получения её результата:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True

Начнём с неправильных данных. В этом случае поле subject будет пустым (ошибка, так как по умолчанию все поля должны быть заполнены), а поле sender содержит неправильный адрес электронной почты:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
Form.errors

Обратитесь к атрибуту errors для получения словаря с сообщениями об ошибках:

>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

In this dictionary, the keys are the field names, and the values are lists of strings representing the error messages. The error messages are stored in lists because a field can have multiple error messages.

Обращаться к атрибуту errors можно без предварительного вызова методе call is_valid(). Данные формы будут проверены при вызове метода is_valid() или при обращении к errors.

Процедуры проверки выполняются один раз, независимо от количества обращений к атрибуту errors или вызова метода is_valid(). Это означает, что если проверка данных имеет побочное влияние на состояние формы, то оно проявится только один раз.

Form.errors.as_data()

Возвращает dict, который содержит поля и объекты ValidationError.

>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}

Используйте этот метод, если вам необходимо определить тип ошибки по её code. Это позволяет переопределить сообщение об ошибке, или добавить дополнительную логику обработки некоторых ошибок. Также позволяет преобразовать ошибки в другой формат (например, XML). Например, as_json() использует as_data().

Метод as_data() добавлен для обратной совместимости. В предыдущих версиях объекты ValidationError терялись при получении сообщения с ошибкой и добавления его в словарь Form.errors. В идеале Form.errors должен содержать объекты ValidationError и метод с префиксом as_ должен преобразовать их в текстовые сообщения, но нам пришлось пойти другим путем, чтобы не сломать существующий код, который ожидает получить сообщения с ошибками из Form.errors.

Form.errors.as_json(escape_html=False)

Возвращает ошибки в JSON формате.

>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}

By default, as_json() does not escape its output. If you are using it for something like AJAX requests to a form view where the client interprets the response and inserts errors into the page, you’ll want to be sure to escape the results on the client-side to avoid the possibility of a cross-site scripting attack. You can do this in JavaScript with element.textContent = errorText or with jQuery’s $(el).text(errorText) (rather than its .html() function).

Если вы не хотите использовать экранирование на стороне клиента, можете передать аргумент escape_html=True и все ошибки будет экранированы, теперь их можно вставлять непосредственно в HTML.

Form.errors.get_json_data(escape_html=False)

Returns the errors as a dictionary suitable for serializing to JSON. Form.errors.as_json() returns serialized JSON, while this returns the error data before it’s serialized.

The escape_html parameter behaves as described in Form.errors.as_json().

Form.add_error(field, error)

Этот метод позволяет добавить ошибки конкретному полю в методе Form.clean(), или вне формы. Например, из представления.

Аргумент field указывает поле, для которого необходимо добавить ошибку. Если равен None, ошибка будет добавлена к общим ошибкам формы, которые можно получить через метод Form.non_field_errors().

The error argument can be a string, or preferably an instance of ValidationError. See Вызов ValidationError for best practices when defining form errors.

Обратите внимание, Form.add_error() автоматически удалит поле из cleaned_data.

Form.has_error(field, code=None)

Этот метод возвращает True, если поле содержит ошибку с кодом code. Если code равен None, вернет True, если поле содержит любую ошибку.

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

Form.non_field_errors()

Этот метод возвращает список ошибок из Form.errors, которые не привязаны к конкретному полю. Сюда входят ошибки ValidationError, вызванные в Form.clean(), и ошибки, добавленные через Form.add_error(None, "...").

Поведение незаполненных форм

Бессмысленно проводить проверку незаполненной формы, но покажем, что происходит с такой формой:

>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}

Динамические начальные значения

Form.initial

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

Для этого следует использовать аргумент initial конструктора класса Form. Этот аргумент, если он передан, должен содержать словарь, связывающий имена полей с их значениями. Указывайте в словаре только те поля, значения которых вы желаете определить. Например:

>>> f = ContactForm(initial={'subject': 'Hi there!'})

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

If a Field defines initial and you include initial when instantiating the Form, then the latter initial will have precedence. In this example, initial is provided both at the field level and at the form instance level, and the latter gets precedence:

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial='class')
...     url = forms.URLField()
...     comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required></td></tr>
Form.get_initial_for_field(field, field_name)

Use get_initial_for_field() to retrieve initial data for a form field. It retrieves data from Form.initial and Field.initial, in that order, and evaluates any callable initial values.

Проверяем какие данные формы были изменены

Form.has_changed()

Используйте метод has_changed(), если необходимо проверить были ли изначальные данные изменены.

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False

После отправки формы, мы создаем ее, предоставляя изначальные данные, и теперь может проверить изменились ли они:

>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()

has_changed() вернет True, если данные из request.POST отличаются от данных из initial, иначе вернет False. Результат вычисляется путем вызова Field.has_changed() для каждого поля формы.

Form.changed_data

Атрибут changed_data возвращает список полей, чии значения из переданных данных (обычно request.POST) отличаются от значений из initial. Возвращает пустой список, если данные не поменялись.

>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
...     print("The following fields changed: %s" % ", ".join(f.changed_data))
>>> f.changed_data
['subject', 'message']

Обращение к полям из формы

Form.fields

Вы можете обращаться к полям объекта Form, используя атрибут fields:

>>> for row in f.fields.values(): print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields['name']
<django.forms.fields.CharField object at 0x7ffaac6324d0>

Вы можете изменить поле:

>>> f.as_table().split('\n')[0]
'<tr><th>Name:</th><td><input name="name" type="text" value="instance" required></td></tr>'
>>> f.fields['name'].label = "Username"
>>> f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="instance" required></td></tr>'

Будьте осторожны, не изменяйте атрибут base_fields т.к. эти изменения повлияют на все экземпляры ContactForm в текущем процессе Python:

>>> f.base_fields['name'].label = "Username"
>>> another_f = CommentForm(auto_id=False)
>>> another_f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="class" required></td></tr>'

Доступ к «чистым» данным

Form.cleaned_data

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

Например, класс DateField нормализует введённое значение к объекту datetime.date. Независимо от того, передали ли вы строку в формате '1994-07-15', объект datetime.date или число в других форматах, DateField всегда преобразует его в объект datetime.date, если при этом не произойдёт ошибка.

После создания экземпляра Form, привязки данных и их проверки, вы можете обращаться к «чистым» данным через атрибут cleaned_data:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

Note that any text-based field – such as CharField or EmailField – always cleans the input into a string. We’ll cover the encoding implications later in this document.

Если данные не прошли проверку, то атрибут cleaned_data будет содержать только значения тех полей, что прошли проверку:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}

Атрибут cleaned_data всегда содержит только данные для полей, определённых в классе Form, даже если вы передали дополнительные данные при определении Form. В этом примере, мы передаём набор дополнительных полей в конструктор ContactForm, но cleaned_data содержит только поля формы:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True,
...         'extra_field_1': 'foo',
...         'extra_field_2': 'bar',
...         'extra_field_3': 'baz'}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

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

>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
...     nick_name = forms.CharField(required=False)
>>> data = {'first_name': 'John', 'last_name': 'Lennon'}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}

В приведённом выше примере, значением атрибута cleaned_data для поля nick_name является пустая строка, так как nick_name – это CharField, а CharField рассматривает пустые значения как пустые строки. Каждый тип поля знает, что такое «пустое» значение, т.е. для DateField – это None, на не пустая строка. Для получения подробностей о поведении каждого типа поля обращайте внимание на заметку «Пустое значение» для каждого поля в разделе «Встроенные поля», которые приведён далее.

Вы можете написать код для выполнения проверки определённых полей формы (используя их имена) или для проверки всей формы (рассматривая её как комбинацию полей). Подробная информация изложена в Проверка форм и полей формы.

Выдача формы в виде HTML

The second task of a Form object is to render itself as HTML. To do so, print it:

>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

If the form is bound to data, the HTML output will include that data appropriately. For example, if a field is represented by an <input type="text">, the data will be in the value attribute. If a field is represented by an <input type="checkbox">, then that HTML will include checked if appropriate:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></td></tr>

По умолчанию в HTML форма представляется в виде таблицы, т.е. каждое поле обёрнуто в <tr>. Обратите внимание на:

  • Для гибкости, выводимый код не включает в себя ни теги <table> и </table>, ни теги <form> and </form>, ни тег <input type="submit">. Не забывайте их добавлять.
  • Каждый тип поля имеет стандартное HTML представление. Тип CharField``представлен как ``<input type="text">, а EmailField как <input type="email">. Тип BooleanField представлен <input type="checkbox">. Следует отметить, что эти представления достаточно гибкие, так как вы можете влиять на них, указав для поля виджет. Далее мы покажем как это делается.
  • Атрибут name каждого тега совпадает напрямую с именем атрибута в классе ContactForm.
  • Текстовая метка каждого поля, т.е. 'Subject:', 'Message:' и 'Cc myself:', генерируется из имени поля, конвертируя символы подчёркивания в пробелы и переводя первый символ в верхний регистр. Также, вы можете явно назначить текстовую метку для поля.
  • Каждая текстовая метка выводится с помощью тега <label>, который указывает на соответствующее поле формы с помощью атрибута id. Атрибут id генерируется путём добавления префикса 'id_' к имени поля. Атрибуты id и теги <label> включаются в HTML представление формы по умолчанию, но можете изменить такое поведение формы.
  • The output uses HTML5 syntax, targeting <!DOCTYPE html>. For example, it uses boolean attributes such as checked rather than the XHTML style of checked='checked'.

Although <table> output is the default output style when you print a form, other output styles are available. Each style is available as a method on a form object, and each rendering method returns a string.

as_p()

Form.as_p()

Метод as_p() представляет форму в виде последовательности тегов <p>, по одному на каждое поле:

>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<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> <input type="text" name="message" id="id_message" required></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>

as_ul()

Form.as_ul()

Метод as_ul() представляет форму в виде последовательности тегов <li>, по одному на каждое поле. Представление не включает в себя теги <ul> или </ul>, таким образом вы можете указать любой атрибут для тега <ul>:

>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>

as_table()

Form.as_table()

Наконец, метод as_table() выводит форму в виде таблицы. Этот метод используется по умолчанию:

>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

Стилизация обязательных полей или полей с ошибкой

Form.error_css_class
Form.required_css_class

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

The Form class has a couple of hooks you can use to add class attributes to required rows or to rows with errors: set the Form.error_css_class and/or Form.required_css_class attributes:

from django import forms

class ContactForm(forms.Form):
    error_css_class = 'error'
    required_css_class = 'required'

    # ... and the rest of your fields here

После этого к полям будут добавлены классы "error" и/или "required". В результате HTML код будет выглядеть таким образом:

>>> f = ContactForm(data)
>>> print(f.as_table())
<tr class="required"><th><label class="required" for="id_subject">Subject:</label>    ...
<tr class="required"><th><label class="required" for="id_message">Message:</label>    ...
<tr class="required error"><th><label class="required" for="id_sender">Sender:</label>      ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...
>>> f['subject'].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f['subject'].label_tag(attrs={'class': 'foo'})
<label for="id_subject" class="foo required">Subject:</label>

Настройка тегов <label> и атрибута id

Form.auto_id

По умолчанию методы для рендеринга форма включают:

  • HTML атрибуты id элементов формы.
  • Тег <label> определяет, с каким элементом формы связана текущая текстовая метка. Это небольшое уточнение делает форму более открытой для устройств. Советуем использовать теги <label>.

Атрибуты id создаются с помощью добавления префикса id_ к именам полей формы. Это поведение является изменяемым, таким образом вы можете изменить его или вообще удалить эти атрибуты и тег <label> из формы.

Используйте аргумент auto_id конструктора Form для управления поведением меток и их идентификаторов. Этот аргумент может принимать значения True, False или строку.

Если auto_id равен False, тогда форма не содержит ни тегов <label>, ни атрибутов id:

>>> f = ContactForm(auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" required></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" required></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself"></td></tr>
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" required></p>
<p>Sender: <input type="email" name="sender" required></p>
<p>Cc myself: <input type="checkbox" name="cc_myself"></p>

If auto_id is set to True, then the form output will include <label> tags and will use the field name as its id for each form field:

>>> f = ContactForm(auto_id=True)
>>> print(f.as_table())
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" required></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" required></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself"></td></tr>
>>> print(f.as_ul())
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" required></li>
<li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself"></li>
>>> print(f.as_p())
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" required></p>
<p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself"></p>

Если auto_id является строкой, которая содержит символ форматирования '%s', тогда форма будет содержать теги <label> и будет генерировать id атрибуты, используя эту строку. Например, для строки 'field_%s', поле с именем subject для атрибута id получит значение 'field_subject'. Вернёмся к нашему примеру:

>>> f = ContactForm(auto_id='id_for_%s')
>>> print(f.as_table())
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" required></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" required></td></tr>
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></td></tr>
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>
>>> print(f.as_p())
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" required></p>
<p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" required></p>
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></p>

Если auto_id равно любому другому значимому значению (например, строка, которая не содержит символа форматирования), тогда библиотека будет рассматривать это значение как True.

По умолчанию, auto_id имеет значение 'id_%s'.

Form.label_suffix

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

Есть возможность заменить это двоеточие на другой символ или вообще убрать его. Для этого надо воспользоваться параметром label_suffix:

>>> f = ContactForm(auto_id='id_for_%s', label_suffix='')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>
>>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>

Следует отметить, что суффикс добавляется к метке только в том случае, когда последний символ метки не является пунктуационным, т.е. не принадлежит списку (., !, ? or :).

Поля могут также могут указывать свои label_suffix. У этого значение приоритет выше, чем у Form.label_suffix. Этот суфикс может быть переопределен во время выполнения через параметр label_suffix атрибута label_tag().

Form.use_required_attribute

When set to True (the default), required form fields will have the required HTML attribute.

Formsets instantiate forms with use_required_attribute=False to avoid incorrect browser validation when adding and deleting forms from a formset.

Configuring the rendering of a form’s widgets

Form.default_renderer

Specifies the renderer to use for the form. Defaults to None which means to use the default renderer specified by the FORM_RENDERER setting.

You can set this as a class attribute when declaring your form or use the renderer argument to Form.__init__(). For example:

from django import forms

class MyForm(forms.Form):
    default_renderer = MyRenderer()

or:

form = MyForm(renderer=MyRenderer())

Порядок полей

In the as_p(), as_ul() and as_table() shortcuts, the fields are displayed in the order in which you define them in your form class. For example, in the ContactForm example, the fields are defined in the order subject, message, sender, cc_myself. To reorder the HTML output, change the order in which those fields are listed in the class.

Есть и другие способы переопределить порядок полей в форме:

Form.field_order

По умолчанию Form.field_order=None, при этом поля формы будут в порядке их определения. Если field_order содержит список названий полей формы, поля будут выведены в указанном порядке, а оставшиеся – в порядке по умолчанию. Неизвестные названия полей будут проигнорированы. Это позволяете удалить поле в классе-наследние, установив его в None, и при это вам не нужно переопределять field_order.

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

Form.order_fields(field_order)

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

Отображение ошибок

При отображении заполненного объекта Form, процесс генерации HTML кода автоматически выполняет проверку данных формы, если она ещё не была произведена. Если проверка выявила ошибки, то HTML код формы будет включать в себя список ошибок в виде списка <ul class="errorlist"> у соответствующего поля. Точная позиция списка сообщений с ошибками проверки зависит от метода, который вы используете:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" required></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required></td></tr>
<tr><th>Cc myself:</th><td><input checked type="checkbox" name="cc_myself"></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" value="Hi there" required></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required></li>
<li>Cc myself: <input checked type="checkbox" name="cc_myself"></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" value="Hi there" required></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>

Настройка формата списка ошибок

By default, forms use django.forms.utils.ErrorList to format validation errors. If you’d like to use an alternate class for displaying errors, you can pass that in at construction time:

>>> from django.forms.utils import ErrorList
>>> class DivErrorList(ErrorList):
...     def __str__(self):
...         return self.as_divs()
...     def as_divs(self):
...         if not self: return ''
...         return '<div class="errorlist">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
>>> f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" value="Hi there" required></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="email" name="sender" value="invalid email address" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>

Точное управление выводом

The as_p(), as_ul(), and as_table() methods are shortcuts – they’re not the only way a form object can be displayed.

class BoundField

Используется для отображения HTML или для доступа к атрибутам отдельного поля экземпляра Form.

The __str__() method of this object displays the HTML for this field.

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

>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" required>

Для получения всех объектов BoundField, пройдите циклом по форме:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">

Вывод указанного поля соответствует настройке auto_id:

>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" required>

Атрибуты BoundField

BoundField.auto_id

The HTML ID attribute for this BoundField. Returns an empty string if Form.auto_id is False.

BoundField.data

Это свойство возвращает данные для текущего BoundField, полученные из метода value_from_datadict() виджета, или None, если данные отсутствуют:

>>> unbound_form = ContactForm()
>>> print(unbound_form['subject'].data)
None
>>> bound_form = ContactForm(data={'subject': 'My Subject'})
>>> print(bound_form['subject'].data)
My Subject
BoundField.errors

Объект со свойствами списка, который при отображении представляется в виде <ul class="errorlist">:

>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f['message'].errors
['This field is required.']
>>> print(f['message'].errors)
<ul class="errorlist"><li>This field is required.</li></ul>
>>> f['subject'].errors
[]
>>> print(f['subject'].errors)

>>> str(f['subject'].errors)
''
BoundField.field

Экземпляр Field из класса формы, который оборачивает текущий BoundField.

BoundField.form

Экземпляр Form, к которому привязан текущий BoundField.

BoundField.help_text

help_text поля.

BoundField.html_name

Название, которое будет использоваться в HTML атрибуте name виджета. Учитывает prefix формы.

BoundField.id_for_label

Этот атрибут рендерит ID поля. Например, вы можете использовать его, если вы самостоятельно создаете <label> для поля (несмотря на то, что label_tag() делает это для вас):

<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}

По умолчанию, он равен названию поля с префиксом id_id_my_field» для примера выше). Вы можете изменить ID, передав его в параметре attrs виджета. Например, определив поле следующим образом:

my_field = forms.CharField(widget=forms.TextInput(attrs={'id': 'myFIELD'}))

и используя пример выше, получите следующий результат:

<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
BoundField.is_hidden

Возвращает True, если виджет BoundField“ скрыт.

BoundField.label

label поля. Используется в label_tag().

BoundField.name

Название текущего поля в форме:

>>> f = ContactForm()
>>> print(f['subject'].name)
subject
>>> print(f['message'].name)
message

Методы BoundField

BoundField.as_hidden(attrs=None, **kwargs)

Возвращает HTML строку, которая представляет текущее поля как <input type="hidden">.

**kwargs передается в as_widget().

Этот метод в основном используется внутренним кодом. Вам следует использовать виджет вместо него.

BoundField.as_widget(widget=None, attrs=None, only_initial=False)

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

only_initial используется Django и не должен указываться явно.

BoundField.css_classes()

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

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes()
'required'

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

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes('foo bar')
'foo bar required'
BoundField.label_tag(contents=None, attrs=None, label_suffix=None)

Чтобы отдельно отрендерить label поля, вызовите метод label_tag():

>>> f = ContactForm(data={'message': ''})
>>> print(f['message'].label_tag())
<label for="id_message">Message:</label>

Вы можете передать параметр contents, который заменит автоматически генерируемый тег <label>. Необязательный параметр attrs, который является словарем, может передавать дополнительные атрибуты для тега <label>.

Сгенерированный HTML содержит label_suffix формы (двоеточие по умолчанию), или label_suffix поля, если этот атрибут установлен. Необязательный аргумент label_suffix позволяет переопределить установленный ранее суфикс. Например, можно использовать пустую строку, чтобы скрыть метку поля. Если это необходимо сделать в шаблоне, создайте собственный фильтр, который позволяет передавать значение в label_tag.

BoundField.value()

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

>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data={'subject': 'hi'}, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi

Настройка BoundField

Если вам необходима дополнительная информация про поле формы в шаблоне, и наследоваться от Field не достаточно, вы можете переопределить BoundField.

В собственном поле формы можно переопределить метод get_bound_field():

Field.get_bound_field(form, field_name)

Принимает экземпляр Form и название поля. Возвращаемое значение будет использоваться при доступе к полю в шаблоне. Обычно это экземпляр BoundField.

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

class GPSCoordinatesBoundField(BoundField):
    @property
    def country(self):
        """
        Return the country the coordinates lie in or None if it can't be
        determined.
        """
        value = self.value()
        if value:
            return get_country_from_coordinates(value)
        else:
            return None

class GPSCoordinatesField(Field):
    def get_bound_field(self, form, field_name):
        return GPSCoordinatesBoundField(form, self, field_name)

Теперь вы можете получить страну в шаблоне через {{ form.coordinates.country }}.

Привязка загруженных файлов к форме

Работа с формами, которые содержат поля FileField и ImageField немного отличается от работы с обычными формами.

Сначала, для того, чтобы загружать файлы, вам потребуется назначить форме атрибут enctype:

<form enctype="multipart/form-data" method="post" action="/foo/">

Затем, при использовании формы следует привязывать данные к форме. Файлы обрабатываются отдельно от данных формы. Таким образом, если ваша форма содержит поля FileField и ImageField, потребуется указать второй аргумент у конструктора. Если мы добавим в нашу ContactForm поле ImageField с именем mugshot, придётся сделать следующее:

# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)}
>>> f = ContactFormWithMugshot(data, file_data)

На практике вы будете просто указывать request.FILES в качестве источника данных файлов аналогично тому, как вы указываете request.POST в качестве источника данных формы:

# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)

Constructing an unbound form is the same as always – omit both form data and file data:

# Unbound form with an image field
>>> f = ContactFormWithMugshot()

Определение таких форм

Form.is_multipart()

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

>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True

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

{% if form.is_multipart %}
    <form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
    <form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>

Наследование форм

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

При наследовании определённого класса Form, результирующий класс будет обладать всеми полями родительского класса (-ов), включая поля, которые определены в нём самом.

В этом примере, ContactFormWithPriority содержит все поля из ContactForm, и собственное поле, priority. Поля ContactForm выводятся первыми:

>>> class ContactFormWithPriority(ContactForm):
...     priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
<li>Priority: <input type="text" name="priority" required></li>

Также можно наследовать несколько форм, рассматривая формы как «смесь»(mixin). В этом примере форма BeatleForm наследует формы PersonForm и InstrumentForm (именно в этом порядке) и её список полей включает в себя поля из родительских классов:

>>> from django import forms
>>> class PersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
>>> class InstrumentForm(forms.Form):
...     instrument = forms.CharField()
>>> class BeatleForm(InstrumentForm, PersonForm):
...     haircut_type = forms.CharField()
>>> b = BeatleForm(auto_id=False)
>>> print(b.as_ul())
<li>First name: <input type="text" name="first_name" required></li>
<li>Last name: <input type="text" name="last_name" required></li>
<li>Instrument: <input type="text" name="instrument" required></li>
<li>Haircut type: <input type="text" name="haircut_type" required></li>

Добавлена возможность декларативно удалить Field, унаследованное от родительского класса, указав None. Например:

>>> from django import forms

>>> class ParentForm(forms.Form):
...     name = forms.CharField()
...     age = forms.IntegerField()

>>> class ChildForm(ParentForm):
...     name = None

>>> list(ChildForm().fields)
['age']

Префиксы для форм

Form.prefix

Вы можете размещать несколько форм в одном теге <form>. Для размещения каждой формы в собственном пространстве имён следует использовать именованный аргумент prefix:

>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother.as_ul())
<li><label for="id_mother-first_name">First name:</label> <input type="text" name="mother-first_name" id="id_mother-first_name" required></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" required></li>
>>> print(father.as_ul())
<li><label for="id_father-first_name">First name:</label> <input type="text" name="father-first_name" id="id_father-first_name" required></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" required></li>

Префикс также можно указать в классе формы:

>>> class PersonForm(forms.Form):
...     ...
...     prefix = 'person'