Spec-Zone.ru › Django 1.9

API форм

О документе

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

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

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

  • Если он связан с набором данных, он может валидировать эти данные и отобразить форму в HTML, отобразив данные в 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.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 в представление формы, где клиент интерпретирует ответ и вставляет ошибки на страницу, вам следует убедиться в экранировании результатов на стороне клиента, чтобы избежать возможности межсайтовой атаки с использованием сценариев. Это легко сделать, используя 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, которые не связаны с конкретным полем. Это включает в себя ValidationError, генерируемые в Form.clean(), и ошибки, добавленные с помощью Form.add_error(None, "...").

Поведение несвязанных форм

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

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

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

Form.initial

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

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

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

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

Обратите внимание, что если 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" /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></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" /></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" /></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" /></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.forms import Form
>>> class OptionalPersonForm(Form):
...     first_name = CharField()
...     last_name = CharField()
...     nick_name = 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" /></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="email" 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="email" 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> и </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" /></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="email" 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>, где каждый <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" /></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="email" 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="email" 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() выводит форму в виде HTML-таблицы <table>. Это точно так же, как 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" /></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="email" 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="email" 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:

from django.forms import Form

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

Тег required_css_class также будет добавлен к тегу <label>, как показано выше.

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

Form.auto_id

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

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

Значения атрибута id генерируются путём добавления префикса id_ к именам полей формы. Однако это поведение настраивается, если вы хотите изменить соглашение id или полностью убрать атрибуты HTML id и теги <label> <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" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
<tr><th>Sender:</th><td><input type="email" 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="email" 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="email" 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="email" 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="email" 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="email" 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>, и будет генерировать атрибуты <label> на основе строки формата. Например, для строки формата '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="email" 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="email" 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="email" 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 установлено в любое другое истинное значение — например, в строку, не содержащую %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" /></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="email" 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="email" 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>

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

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

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

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

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

Form.field_order

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

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

Form.order_fields(field_order)

Вы можете переупорядочить поля в любое время, используя 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" /></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="email" 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="email" 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="email" name="sender" value="invalid email address" /></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" /></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="email" 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 [source]

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

Метод __str__() (__unicode__ в Python 2) этого объекта отображает 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="email" 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" />

Атрибуты объекта 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" />
>>> 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.

END_OF_DOCUMENT_MARKER
BoundField.id_for_label

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

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

По умолчанию это будет имя поля, опережаемое префиксом id_ (“id_my_field” для примера выше). Вы можете изменить идентификатор, установив attrs на виджете поля. Например, объявив поле так:

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

и используя шаблон выше, отобразится что-то вроде:

<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" />
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.

Метка включает required_css_class, если применимо.

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

Если вам нужно получить доступ к дополнительной информации о поле формы в шаблоне, и использование подкласса 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" /></li>
<li>Message: <input type="text" name="message" /></li>
<li>Sender: <input type="email" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li>

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

>>> from django.forms import Form
>>> 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>

Можно декларативно удалить поле 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>. Чтобы предоставить каждой форме собственное пространство имен, используйте ключевое слово 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>

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

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

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

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

Spec-Zone.ru

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