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 e-mail 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, всегда преобразует текст в юникодную строку. Мы рассмотрим применения кодировок далее.

Если данные не прошли проверку, то экземпляр Form не будет иметь атрибут cleaned_data:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
Traceback (most recent call last):
...
AttributeError: 'ContactForm' object has no attribute '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'}

Атрибут cleaned_data всегда содержит ключ и значение для всех полей, определённых в Form, даже если данные не содержат значения для поля, которое не является обязательным. В этом примере, словарь данных не содержит значение для поля 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 имеет ряд возможностей, которые вы можете использовать для добавления атрибутов 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> и использует имена полей в качестве значений атрибутов tags and will simply 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" /></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 e-mail 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 e-mail 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 e-mail 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 e-mail 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.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

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

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

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

Если вы пишете представления и шаблоны в расчёте на их неоднократное использование в будущем, вы не можете знать будет ли ваша форма обычной или будет работать с файлами. В этом случае вам поможет метод 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>