Spec-Zone.ru › Django 2.0

API форм

О документе

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

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

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

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

Для создания экземпляра несвязанной Form, просто проинициализируйте класс:

>>> f = ContactForm()

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

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

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

Form.is_bound

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

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

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

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

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

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

Form.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.']}

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

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

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

Form.errors.as_data()

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

>>> 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.errors.get_json_data(escape_html=False)
Новое в Django 2.0.

Возвращает ошибки в виде словаря, подходящего для сериализации в JSON. Form.errors.as_json() возвращает сериализованный JSON, а это возвращает данные ошибки перед сериализацией.

Параметр escape_html ведет себя так, как описано в Form.errors.as_json().

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" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>
Form.get_initial_for_field(field, field_name)
Новое в Django 1.11.

Используйте get_initial_for_field() для получения начальных данных для поля формы. Оно извлекает данные из Form.initial и Field.initial в таком порядке и вычисляет любые вызываемые начальные значения.

Проверка изменений данных формы

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 – всегда очищает ввод в строку. Мы рассмотрим последствия кодирования позже в этом документе.

Если ваши данные не проходят проверку, словарь 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, если это необходимо:

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

Атрибут checked был изменён на использование синтаксиса булевых значений HTML5 вместо checked="checked".

Этот вывод по умолчанию — 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> включаются в вывод по умолчанию для соблюдения лучших практик, но вы можете изменить это поведение.
  • Вывод использует синтаксис HTML5, ориентированный на <!DOCTYPE html>. Например, он использует булевые атрибуты, такие как checked, а не XHTML-стиль checked='checked'.

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

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 имеет несколько крючков, которые можно использовать для добавления атрибутов к требуемым строкам или строкам с ошибками: просто установите атрибуты 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 получит значение '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

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

Formsets инициализируют формы с use_required_attribute=False для предотвращения неправильной проверки браузера при добавлении и удалении форм из набора форм.

Настройка рендеринга виджетов формы

Form.default_renderer
Новая функция в Django 1.11.

Определяет рендерер для использования формы. По умолчанию None, что означает использование рендерера по умолчанию, указанного в настройке FORM_RENDERER.

Вы можете задать это как атрибут класса при объявлении формы или использовать аргумент renderer к Form.__init__(). Например:

from django import forms

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

или:

form = MyForm(renderer=MyRenderer())

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

В 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" required /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" required /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required /></td></tr>
<tr><th>Cc myself:</th><td><input checked type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" required /></li>
<li>Message: <input type="text" name="message" value="Hi there" required /></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required /></li>
<li>Cc myself: <input checked type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
<p>Message: <input type="text" name="message" value="Hi there" required /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" required /></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself" /></p>

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

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

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

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

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

class BoundField [source]

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

Метод __str__() этого объекта отображает 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

Если вам нужно получить доступ к дополнительной информации о поле формы в шаблоне, а наследование от 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

>>> list(ChildForm().fields)
['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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/ref/forms/api/

Spec-Zone.ru

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