API форм

About this document

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

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

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

  • Если он заполнен, то у него есть возможность валидации полученных данных и генерации заполненной формы в виде HTML кода.

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

class Form

Для создания незаполненного экземпляра Form, просто создайте объект:

>>> 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.is_valid()

Главной задачей объекта a 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': [u'Enter a valid email address.'], 'subject': [u'This field is required.']}

В этом словаре, ключами являются имена полей, а значениями – списки юникодных строк, представляющих сообщения об ошибках. Сообщения хранятся в виде списков, так как поле может иметь множество таких сообщений.

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

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

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

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

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

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

Form.initial

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

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

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

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

Следует отметить, что если класс Field определяет атрибут initial и вы используете аргумент initial при создании экземпляра Form, то последний initial будет иметь преимущество. В этом примере, initial указан на уровнях поля и экземпляра формы, и последний будет иметь преимущество:

>>> 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" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></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': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}

Следует отметить, что любое текстовое поле, такое как CharField или EmailField, всегда преобразует текст в юникодную строку. Мы рассмотрим применения кодировок далее.

Если данные не прошли проверку, то атрибут 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': u'Hi there'}
Изменено в Django 1.5.

До Django 1.5 атрибут cleaned_data вообще не определялся, если форма не проходила проверку.

Атрибут 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': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}

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

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

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

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

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

Второй задачей объекта Form является представление себя в виде HTML кода. Для этого объект надо просто “распечатать”:

>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></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>

Если форма заполнена данными, то полученный HTML код будет включать себя эти данные. Например, если поле представлено тегом <input type="text">, то его данные будут подставлены в атрибут value. Если поле представлено тегом <input type="checkbox">, то HTML код будет содержать checked="checked":

>>> 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" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></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="checked" /></td></tr>

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

  • Для гибкости, выводимый код не включает в себя ни теги <table> и </table>, ни теги <form> and </form>, ни тег <input type="submit">. Не забывайте их добавлять.

  • Каждый тип поля имеет стандартное HTML представление. Тип CharField и EmailField представлен как <input type="text">. Тип BooleanField представлен <input type="checkbox">. Следует отметить, что эти представления достаточно гибкие, так как вы можете влиять на них, указав для поля виджет. Далее мы покажем как это делается.

  • Атрибут name каждого тега совпадает напрямую с именем атрибута в классе ContactForm.

  • Текстовая метка каждого поля, т.е. 'Subject:', 'Message:' и 'Cc myself:', генерируется из имени поля, конвертируя символы подчёркивания в пробелы и переводя первый символ в верхний регистр. Также, вы можете явно назначить текстовую метку для поля.

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

Несмотря на то, что по умолчанию форма выводится в табличном виде, существует ряд других представлений. Каждое представление доступно через метод объекта формы, возвращая объект Unicode.

as_p()

Form.as_p()

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

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

as_ul()

Form.as_ul()

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

>>> f = ContactForm()
>>> f.as_ul()
u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></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" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></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()
u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></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.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></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

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

Класс Form имеет ряд возможностей, которые вы можете использовать для добавления атрибутов class к обязательным полям и полям с ошибками: просто определите атрибут Form.error_css_class и/или Form.required_css_class attributes:

class ContactForm(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 for="id_subject">Subject:</label>    ...
<tr class="required"><th><label for="id_message">Message:</label>    ...
<tr class="required error"><th><label for="id_sender">Sender:</label>      ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...

Настройка тегов <label>

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

По умолчанию, методы представления формы назначают элементам формы атрибуты id и их текстовым меткам. Атрибуты 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" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="text" name="sender" /></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" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" /></p>
<p>Sender: <input type="text" name="sender" /></p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>

Если auto_id равен True, тогда форма содержит теги <label> и использует имена полей в качестве значений атрибутов тегов, название поля будет использоваться как id:

>>> 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" /></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></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" /></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
<li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></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" /></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
<p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></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" /></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
<tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></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" /></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></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" /></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
<p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></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'.

Обычно при отображении после имён меток добавляется двоеточие (:). Есть возможность заменить это двоеточие на другой символ или вообще убрать его. Для этого надо воспользоваться параметром 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" /></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender</label> <input type="text" name="sender" id="id_for_sender" /></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" /></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li>
<li><label for="id_for_sender">Sender -></label> <input type="text" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>

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

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

При использовании методов as_p(), as_ul() и as_table() поля отображаются в порядке их определения в классе формы. Например, в нашей форме ContactForm поля определены в порядке subject, message, sender, cc_myself. Для изменения их порядка на форме, просто поменяйте их местами в классе.

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

При отображении заполненного объекта 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" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="text" name="sender" value="invalid email address" /></td></tr>
<tr><th>Cc myself:</th><td><input checked="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" /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="text" name="sender" value="invalid email address" /></li>
<li>Cc myself: <input checked="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" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="text" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>

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

По умолчанию, формы используют django.forms.util.ErrorList для форматирования списка с ошибками проверки. Если вам требуется использовать другой класс для отображения ошибок, вы можете передать его во время создания объекта формы:

>>> from django.forms.util import ErrorList
>>> class DivErrorList(ErrorList):
...     def __unicode__(self):
...         return self.as_divs()
...     def as_divs(self):
...         if not self: return u''
...         return u'<div class="errorlist">%s</div>' % ''.join([u'<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" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="text" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>

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

Методы as_p(), as_ul() и as_table() являются общими методами для ленивых разработчиков. Это не единственные способы отображения формы.

class BoundField

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

Методы __unicode__() и __str__() этого объекта возвращают HTML код данного поля.

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

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

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

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="text" name="sender" id="id_sender" />
<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" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />

Для получения списка ошибок в полях формы следует использовать атрибут errors.

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" />
>>> f['message'].errors
[u'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.label_tag(contents=None, attrs=None)

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

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

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

BoundField.css_classes()

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

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

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

>>> f = ContactForm(data)
>>> f['message'].css_classes('foo bar')
'foo bar required'
BoundField.value()

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

>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi
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" />

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

Работа с формами, которые содержат поля 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)

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

# Unbound form with a 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" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="text" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li>

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

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

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

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" /></li>
<li><label for="id_mother-last_name">Last name:</label> <input type="text" name="mother-last_name" id="id_mother-last_name" /></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" /></li>
<li><label for="id_father-last_name">Last name:</label> <input type="text" name="father-last_name" id="id_father-last_name" /></li>