Spec-Zone.ru › Django 1.10

API форм

О документе

В этом документе подробно рассматривается API форм Django. Сначала следует прочитать введение в работу с формами.

Связанные и несвязанные формы

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

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

Для создания несвязанного объекта 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.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.']}

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

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

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

Form.errors.as_data()

Возвращает словарь, который сопоставляет поля их исходным 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"}]}

По умолчанию as_json() не экранирует свой вывод. Если вы используете его для чего-то вроде запросов AJAX к представлению формы, где клиент интерпретирует ответ и вставляет ошибки на страницу, вам следует убедиться, что вы экранируете результаты на стороне клиента, чтобы избежать возможных атак XSS. Это легко сделать с помощью JavaScript-библиотеки, такой как jQuery — просто используйте $(el).text(errorText) вместо .html().

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

Form.add_error(field, error)

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

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

Аргумент error может быть простой строкой или, предпочтительнее, экземпляром класса ValidationError. См. Поднятие ValidationError для рекомендаций по определению ошибок формы.

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

Form.has_error(field, code=None)

Этот метод возвращает булево значение, определяющее, есть ли ошибка в поле со специфической ошибкой code. Если code равно None, он вернёт True если поле содержит какие-либо ошибки вообще.

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

Form.non_field_errors()

Этот метод возвращает список ошибок из Form.errors, которые не связаны с конкретным полем. Это включает ошибки, возникающие в 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!'})

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

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

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

Используйте метод has_changed() для вашего экземпляра Form, когда вам нужно проверить, были ли данные формы изменены со времени начальных данных.

>>> 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))

Доступ к полям из формы

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>

Вы можете изменить поле экземпляра Form, чтобы изменить способ его отображения в форме:

>>> 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 нормализует входные данные в объект Python 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'}

Обратите внимание, что любое текстовое поле — например, CharField или EmailField — всегда очищает входные данные в строку Unicode. Мы рассмотрим последствия кодирования позже в этом документе.

Если ваши данные не прошли валидацию, словарь 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 вместо пустой строки. Полные сведения о поведении каждого поля в этом случае см. в примечании «Пустое значение» для каждого поля в разделе «Встроенные Field классы» ниже.

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

Вывод форм в HTML

Вторая задача объекта Form — отобразить себя как HTML. Для этого просто вызовите print:

>>> 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>

Если форма связана с данными, 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" 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="checked" /></td></tr>

Этот вывод по умолчанию — HTML-таблица из двух столбцов, с <tr> для каждого поля. Обратите внимание на следующее:

  • Для гибкости вывод не включает теги <table> и </table>, а также теги <form> и </form> или тег <input type="submit">. Вы должны сделать это сами.
  • Каждый тип поля имеет стандартное представление в HTML. CharField представлен как <input type="text">, а EmailField как <input type="email">. BooleanField представлено как <input type="checkbox">. Обратите внимание, что это всего лишь разумные значения по умолчанию; вы можете указать нужный HTML для заданного поля, используя виджеты, о которых мы расскажем вскоре.
  • HTML name для каждого тега берется непосредственно из имени атрибута в классе ContactForm.
  • Текст метки для каждого поля — например, 'Subject:', 'Message:' и 'Cc myself:' — генерируется из имени поля путём преобразования всех нижних подчеркиваний в пробелы и преобразования первой буквы в верхний регистр. Опять же, обратите внимание, что это лишь разумные значения по умолчанию; вы также можете указать метки вручную.
  • Каждая метка текста окружена HTML-тегом <label>, который указывает на соответствующее поле формы через его id. Его id, в свою очередь, генерируется путём добавления 'id_' к имени поля. Атрибуты id и теги <label> включены в вывод по умолчанию, чтобы следовать лучшим практикам, но вы можете изменить это поведение.

Хотя вывод <table> является стилем вывода по умолчанию при вызове print формы, доступны и другие стили вывода. Каждый стиль доступен в виде метода объекта формы, и каждый метод рендеринга возвращает строку Unicode.

as_p()

Form.as_p()

as_p() отображает форму как набор тегов <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>, где каждый <li> содержит одно поле. Он не включает теги <ul> или </ul>, чтобы вы могли указать любые атрибуты HTML в теге <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() выводит форму как HTML-таблицу <table>. Это точно то же самое, что и print. На самом деле, когда вы вызываете print объект формы, он вызывает свой метод 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

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

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

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>

Настройка атрибутов HTML id и тегов <label> элементов формы

Form.auto_id

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

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

Значения атрибута id генерируются путём добавления префикса id_ к именам полей формы. Однако это поведение настраивается, если вы хотите изменить id соглашение или полностью удалить HTML атрибуты id и теги <label>.

Используйте аргумент auto_id конструктору Form для управления поведением id и подписей. Этот аргумент должен быть 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>

Если 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" 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 установлено в любое другое истинное значение – например, в строку, которая не включает %s – библиотека будет действовать так, как если бы 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>

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

Поля также могут определять собственный label_suffix. Это переопределит Form.label_suffix. Суффикс также может быть переопределён во время выполнения с помощью параметра label_suffix к label_tag().

Form.use_required_attribute
Новое в Django 1.10.

Когда установлено в True (по умолчанию), обязательные поля формы будут иметь атрибут required HTML.

Наборы форм инициализируют формы с use_required_attribute=False, чтобы избежать неправильной проверки браузером при добавлении и удалении форм из набора форм.

Примечания по порядку полей

В as_p(), as_ul() и as_table() сокращениях поля отображаются в том порядке, в котором вы их определяете в классе формы. Например, в примере ContactForm поля определяются в порядке subject, message, sender, cc_myself. Чтобы изменить порядок вывода HTML, просто измените порядок, в котором эти поля перечислены в классе.

Существует несколько других способов настройки порядка:

Form.field_order
Новое в Django 1.9.

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

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

Form.order_fields(field_order)
Новое в Django 1.9.

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

Как отображаются ошибки

Если вы отображаете связанный Form объект, рендеринг автоматически запустит валидацию формы, если она ещё не была выполнена, и вывод 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="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="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="checked" type="checkbox" name="cc_myself" /></p>

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

По умолчанию формы используют django.forms.utils.ErrorList для форматирования ошибок валидации. Если вы хотите использовать другой класс для отображения ошибок, вы можете передать его при создании (замените __str__ на __unicode__ в Python 2):

>>> from django.forms.utils import ErrorList
>>> class DivErrorList(ErrorList):
...     def __str__(self):              # __unicode__ on Python 2
...         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="checked" type="checkbox" name="cc_myself" /></p>

Более подробный вывод

Методы as_p(), as_ul(), и as_table() — это просто сокращения; они не единственный способ отображения объекта формы.

class BoundField [source]

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

Метод __str__() (__unicode__ в Python 2) этого объекта отображает HTML для этого поля.

Для получения отдельного 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

Атрибут HTML ID для этого BoundField. Возвращает пустую строку, если Form.auto_id имеет значение 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

Список-подобный объект, который отображается как HTML <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) [source]

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

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

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

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

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

only_initial используется внутренними механизмами Django и не должен устанавливаться явно.

BoundField.css_classes() [source]

При использовании коротких способов отображения Django, CSS-классы используются для указания обязательных полей формы или полей с ошибками. Если вы вручную отображаете форму, вы можете получить доступ к этим 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) [source]

Для отдельного отображения метки тега поля формы вы можете вызвать его метод label_tag():

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

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

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

BoundField.value() [source]

Используйте этот метод для отображения исходного значения этого поля так, как оно отобразилось бы 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

Новое в Django 1.9.

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

Пользовательское поле формы может переопределить get_bound_field():

Field.get_bound_field(form, field_name) [source]

Принимает экземпляр 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, немного сложнее, чем с обычной формой.

Во-первых, для загрузки файлов убедитесь, что элемент <form> правильно определяет enctype как "multipart/form-data":

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

Во-вторых, при использовании формы вам необходимо связать данные файла. Данные файла обрабатываются отдельно от обычных данных формы, поэтому при наличии в форме FileField и ImageField, при связывании формы потребуется второй аргумент. Так, если мы расширим нашу ContactForm, чтобы добавить поле ImageField под названием mugshot, нам нужно связать данные файла, содержащие изображение 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 an image field
>>> f = ContactFormWithMugshot()

Проверка на multipart-формы

Form.is_multipart()

Если вы пишете многократно используемые представления или шаблоны, вы можете не знать заранее, является ли ваша форма multipart-формой. Метод is_multipart() сообщает вам, требуется ли для отправки формы 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>

Можно наследовать от нескольких форм, рассматривая формы как миксины. В этом примере 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

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

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

Form.prefix

Вы можете поместить несколько форм Django в один тег <form>. Чтобы дать каждой 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'
Новое в Django 1.9:

Добавлена возможность указать prefix в классе формы.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.10/ref/forms/api/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API