Spec-Zone.ru › Django 3.2

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

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

Вы можете получить доступ к 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 с помощью element.textContent = errorText или с помощью jQuery’s $(el).text(errorText) (а не его функции .html()).

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

Form.errors.get_json_data(escape_html=False)

Возвращает ошибки в виде словаря, подходящего для сериализации в 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, которые не связаны с конкретным полем. Это включает ValidationErrors, возникающие в 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)

Используйте 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))
>>> f.changed_data
['subject', 'message']

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

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>

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

  • Для гибкости вывод не включает теги <table> и </table>, а также теги <form> и </form> или тег <input type="submit">. Вам нужно это сделать самим.
  • Каждый тип поля имеет стандартное HTML-представление. CharField представлено тегом <input type="text">, а EmailField — тегом <input type="email">. BooleanField(null=False) представлено тегом <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 имеет несколько крючков, которые вы можете использовать для добавления атрибутов class к требуемым строкам или строкам с ошибками: задайте атрибуты Form.error_css_class и/или Form.required_css_class:

from django import forms

class ContactForm(forms.Form):
    error_css_class = 'error'
    required_css_class = 'required'

    # ... and the rest of your fields here

После этого строки получат классы "error" и/или "required" по мере необходимости. HTML будет выглядеть примерно так:

>>> f = ContactForm(data)
>>> print(f.as_table())
<tr class="required"><th><label class="required" for="id_subject">Subject:</label>    ...
<tr class="required"><th><label class="required" for="id_message">Message:</label>    ...
<tr class="required error"><th><label class="required" for="id_sender">Sender:</label>      ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...
>>> f['subject'].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f['subject'].label_tag(attrs={'class': 'foo'})
<label for="id_subject" class="foo required">Subject:</label>

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

Form.auto_id

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

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

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

Используйте аргумент auto_id для конструктора Form для управления поведением id и меток. Этот аргумент должен быть True, False или строкой.

Если auto_id равно False, то вывод формы не будет включать теги <label> и атрибуты id.

>>> f = ContactForm(auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" required></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" required></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself"></td></tr>
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" required></p>
<p>Sender: <input type="email" name="sender" required></p>
<p>Cc myself: <input type="checkbox" name="cc_myself"></p>

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

>>> f = ContactForm(auto_id=True)
>>> print(f.as_table())
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" required></td></tr>
<tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" required></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself"></td></tr>
>>> print(f.as_ul())
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" required></li>
<li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself"></li>
>>> print(f.as_p())
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" required></p>
<p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" required></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself"></p>

Если auto_id установлено на строку, содержащую символ форматирования '%s', вывод формы будет включать теги <label>, и будет генерировать атрибуты id на основе строки форматирования. Например, для строки форматирования 'field_%s', поле с именем subject получит значение атрибута '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.

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

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

Form.default_renderer

Определяет рендерер для использования в форме. По умолчанию 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

Используется для отображения 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.widget_type
Новое в Django 3.1.

Возвращает строчное имя класса обернутого виджета поля, с удаленными любыми заключительными input или widget. Это может быть использовано при построении форм, где макет зависит от типа виджета. Например:

{% for field in form %}
    {% if field.widget_type == 'checkbox' %}
        # render one way
    {% else %}
        # render another way
    {% endif %}
{% endfor %}

Методы BoundField

BoundField.as_hidden(attrs=None, **kwargs)

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

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

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

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

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

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

BoundField.css_classes(extra_classes=None)

Когда вы используете сокращения 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)

Для отдельного отображения тега метки поля формы вы можете вызвать его метод 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()

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

Принимает экземпляр 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/3.2/ref/forms/api/

Spec-Zone.ru

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