Spec-Zone.ru › Django 6.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, сопоставляющий поля с исходными экземплярами 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 или с помощью $(el).text(errorText) из jQuery (а не функции .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 и при создании экземпляра Form вы указываете initial, приоритет будет у последнего 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)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
Form.get_initial_for_field(field, field_name)

Возвращает начальные данные поля формы. Метод получает данные из Form.initial, если они заданы, в противном случае использует Field.initial. Вызываемые значения вычисляются.

Рекомендуется использовать BoundField.initial вместо get_initial_for_field(), поскольку у BoundField.initial более простой интерфейс. Кроме того, в отличие от get_initial_for_field(), BoundField.initial кэширует значения. Это особенно полезно при работе с вызываемыми объектами, возвращаемые значения которых могут меняться (например, datetime.now или uuid.uuid4):

>>> import uuid
>>> class UUIDCommentForm(CommentForm):
...     identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')

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

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>

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

>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
>>> f["subject"].label = "Topic"
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

Не изменяйте атрибут base_fields, поскольку это изменение повлияет на все последующие экземпляры ContactForm в том же процессе Python:

>>> f.base_fields["subject"].label_suffix = "?"
>>> another_f = ContactForm(auto_id=False)
>>> another_f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'

Доступ к «очищенным» данным

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, а CharFields представляют пустые значения пустой строкой. Для каждого типа поля определено собственное «пустое» значение: например, для DateField это None, а не пустая строка. Полное описание поведения каждого поля в этом случае см. в примечании «Пустое значение» для каждого поля в разделе Встроенные классы полей ниже.

Можно написать код для проверки отдельных полей формы (по их именам) или формы целиком (с учётом сочетаний различных полей). Дополнительную информацию см. в разделе Проверка формы и полей.

Вывод форм в формате HTML

Вторая задача объекта Form — отображать себя в формате HTML. Для этого print он:

>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>

Если форма связана с данными, вывод 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)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="foo@example.com" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>

Этот вывод по умолчанию заключает каждое поле в <div>. Обратите внимание на следующее:

  • Для гибкости вывод не включает теги <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'.

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

Отображение по умолчанию

При вызове print формы для её отображения по умолчанию используются следующие методы и атрибуты.

template_name

Form.template_name

Имя шаблона, отображаемого при преобразовании формы в строку, например с помощью print(form) или в шаблоне с помощью {{ form }}.

По умолчанию это свойство возвращает значение form_template_name средства отображения. Чтобы переопределить это значение для определённого класса формы, можно задать имя шаблона в виде строки.

render()

Form.render(template_name=None, context=None, renderer=None)

Метод render вызывается объектом __str__, а также методами Form.as_div(), Form.as_table(), Form.as_p() и Form.as_ul(). Все аргументы необязательны и по умолчанию имеют следующие значения:

  • template_name: Form.template_name
  • context: значение, возвращаемое методом Form.get_context()
  • renderer: значение, возвращаемое методом Form.default_renderer

Передав template_name, можно настроить шаблон, используемый только для одного вызова.

get_context()

Form.get_context()

Возвращает контекст шаблона для отображения формы.

Доступный контекст:

  • form: связанная форма.
  • fields: все связанные поля, кроме скрытых.
  • hidden_fields: все связанные скрытые поля.
  • errors: все ошибки формы, не связанные с полем или связанные со скрытым полем.

template_name_label

Form.template_name_label

Шаблон, используемый для отображения <label> поля при вызове BoundField.label_tag()/legend_tag(). Его можно изменить для отдельной формы, переопределив этот атрибут, или более общим образом — переопределив шаблон по умолчанию. См. также раздел Переопределение встроенных шаблонов форм.

Стили вывода

Рекомендуемый способ изменить стиль вывода формы — задать собственный шаблон формы для всего сайта, отдельной формы или отдельного экземпляра. Примеры см. в разделе Повторно используемые шаблоны форм.

Для обратной совместимости предоставлены следующие вспомогательные функции. Они являются прокси для Form.render(), которому передаётся соответствующее значение template_name.

Примечание

Из шаблонов и стилей вывода, предоставляемых фреймворком, рекомендуется использовать вариант as_div() вместо вариантов as_p(), as_table() и as_ul(), поскольку шаблон реализует <fieldset> и <legend> для группировки связанных полей и упрощает навигацию для пользователей программ чтения с экрана.

Каждая вспомогательная функция связывает метод формы с атрибутом, задающим соответствующее имя шаблона.

as_div()

Form.template_name_div

Шаблон, используемый методом as_div(). По умолчанию: 'django/forms/div.html'.

Form.as_div()

Метод as_div() отображает форму в виде набора элементов <div>, каждый из которых <div> содержит одно поле, например:

>>> f = ContactForm()
>>> f.as_div()

… выводит HTML следующего вида:

<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>

as_p()

Form.template_name_p

Шаблон, используемый методом as_p(). По умолчанию: 'django/forms/p.html'.

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

Шаблон, используемый методом as_ul(). По умолчанию: 'django/forms/ul.html'.

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

Шаблон, используемый методом as_table(). По умолчанию: 'django/forms/table.html'.

Form.as_table()

Метод as_table() отображает форму в виде HTML-<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.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>
<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)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>

Дополнительно изменить отображение строк формы можно с помощью пользовательского BoundField.

Настройка атрибутов 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)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>

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

>>> f = ContactForm(auto_id=True)
>>> print(f)
<div><label for="subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="subject"></div>
<div><label for="message">Message:</label><textarea name="message" cols="40" rows="10" required id="message"></textarea></div>
<div><label for="sender">Sender:</label><input type="email" name="sender" required id="sender"></div>
<div><label for="cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="cc_myself"></div>

Если для auto_id задана строка, содержащая символ формата '%s', вывод формы будет содержать теги <label>, а атрибуты id будут сформированы на основе строки формата. Например, при строке формата 'field_%s' поле с именем subject получит значение id, равное 'field_subject'. Продолжим пример:

>>> f = ContactForm(auto_id="id_for_%s")
>>> print(f)
<div><label for="id_for_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message:</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender:</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>

Если для 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)
<div><label for="id_for_subject">Subject</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
>>> f = ContactForm(auto_id="id_for_%s", label_suffix=" ->")
>>> print(f)
<div><label for="id_for_subject">Subject -&gt;</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message -&gt;</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender -&gt;</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself -&gt;</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>

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

Поля также могут задавать собственный label_suffix. Он имеет приоритет над Form.label_suffix. Суффикс также можно переопределить во время выполнения с помощью параметра label_suffix методов label_tag()/ legend_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 и при создании экземпляра Form указан аргумент field_order, приоритет будет иметь последний 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,
... }
>>> ContactForm(data).as_div()

… выводит HTML следующего вида:

<div>
  <label for="id_subject">Subject:</label>
  <ul class="errorlist" id="id_subject_error"><li>This field is required.</li></ul>
  <input type="text" name="subject" maxlength="100" required aria-invalid="true" aria-describedby="id_subject_error" id="id_subject">
</div>
<div>
  <label for="id_message">Message:</label>
  <textarea name="message" cols="40" rows="10" required id="id_message">Hi there</textarea>
</div>
<div>
  <label for="id_sender">Sender:</label>
  <ul class="errorlist" id="id_sender_error"><li>Enter a valid email address.</li></ul>
  <input type="email" name="sender" value="invalid email address" maxlength="320" required aria-invalid="true" aria-describedby="id_sender_error" id="id_sender">
</div>
<div>
    <label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" checked>
</div>

Шаблоны форм Django по умолчанию связывают ошибки проверки с соответствующим полем ввода с помощью HTML-атрибута aria-describedby, если у поля есть auto_id и не задан пользовательский aria-describedby. Если при определении виджета задан пользовательский aria-describedby, он заменит значение по умолчанию.

Если виджет отображается внутри <fieldset>, атрибут aria-describedby добавляется к этому элементу, в противном случае он добавляется к HTML-элементу виджета (например, <input>).

Изменено в Django 5.2:

Добавлен aria-describedby для связи ошибок с соответствующим полем ввода.

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

class ErrorList(initlist=None, error_class=None, renderer=None, field_id=None) [исходный код]

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

Изменено в Django 5.2:

Добавлен аргумент field_id.

error_class

CSS-классы, используемые при отображении списка ошибок. Все указанные классы добавляются к классу errorlist, используемому по умолчанию.

renderer

Указывает средство отображения, используемое для ErrorList. По умолчанию задано None, то есть используется средство отображения по умолчанию, указанное параметром FORM_RENDERER.

field_id
Добавлено в Django 5.2.

Значение id поля, к которому относятся ошибки. Это позволяет добавить HTML-атрибут id в шаблон ошибки и использовать его для связи ошибок с полем. В шаблоне по умолчанию используется формат id="{{ field_id }}_error", а значение передаётся методом Form.add_error() с помощью атрибута поля auto_id.

template_name

Имя шаблона, используемого при вызове __str__ или render(). По умолчанию задано 'django/forms/errors/list/default.html', которое служит прокси для шаблона 'ul.html'.

template_name_text

Имя шаблона, используемого при вызове as_text(). По умолчанию задано 'django/forms/errors/list/text.html'. Этот шаблон отображает ошибки в виде маркированного списка.

template_name_ul

Имя шаблона, используемого при вызове as_ul(). По умолчанию задано 'django/forms/errors/list/ul.html'. Этот шаблон отображает ошибки в тегах <li>, обёрнутых в <ul> с CSS-классами, заданными атрибутом error_class.

get_context() [исходный код]

Возвращает контекст для отображения ошибок в шаблоне.

Доступный контекст:

  • errors : список ошибок.
  • error_class : строка CSS-классов.
render(template_name=None, context=None, renderer=None)

Метод render вызывается объектом __str__, а также методом as_ul().

Все аргументы необязательны и по умолчанию имеют следующие значения:

  • template_name: значение, возвращаемое атрибутом template_name
  • context: значение, возвращаемое методом get_context()
  • renderer: значение, возвращаемое атрибутом renderer
as_text()

Отображает список ошибок с помощью шаблона, заданного атрибутом template_name_text.

as_ul()

Отображает список ошибок с помощью шаблона, заданного атрибутом template_name_ul.

Чтобы настроить отображение ошибок, можно переопределить атрибут template_name или, в более общем случае, переопределить шаблон по умолчанию. См. также раздел Переопределение встроенных шаблонов форм.

Более детализированный вывод

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

class BoundField [исходный код]

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

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

Вы можете использовать Form.bound_field_class и Field.bound_field_class, чтобы задать разные классы BoundField для формы или отдельного поля соответственно.

Примеры переопределения BoundField см. в разделе Настройка BoundField.

Чтобы получить отдельный объект 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.aria_describedby [исходный код]
Добавлено в Django 5.2.

Возвращает ссылку aria-describedby, связывающую поле с текстом справки и ошибками. Возвращает None, если aria-describedby задан в Widget.attrs, чтобы сохранить определенный пользователем атрибут при отображении формы.

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 aria-invalid="true">
>>> 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)
''

При отображении поля с ошибками для его виджета устанавливается aria-invalid="true", чтобы сообщить пользователям программ чтения с экрана о наличии ошибки.

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()/legend_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.initial [исходный код]

Используйте BoundField.initial, чтобы получить начальные данные для поля формы. Сначала данные извлекаются из Form.initial, если они там есть, иначе используется Field.initial. Вызываемые значения вычисляются. Дополнительные примеры см. в разделе Начальные значения формы.

BoundField.initial кэширует возвращаемое значение. Это особенно полезно при работе с вызываемыми объектами, возвращаемые значения которых могут изменяться (например, datetime.now или uuid.uuid4):

>>> from datetime import datetime
>>> class DatedCommentForm(CommentForm):
...     created = forms.DateTimeField(initial=datetime.now)
...
>>> f = DatedCommentForm()
>>> f["created"].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)
>>> f["created"].initial
datetime.datetime(2021, 7, 27, 9, 5, 54)

Рекомендуется использовать BoundField.initial вместо get_initial_for_field().

BoundField.is_hidden [исходный код]

Возвращает True, если виджет этого объекта BoundField является скрытым.

BoundField.label

label поля. Используется в label_tag()/legend_tag().

BoundField.name

Имя этого поля в форме:

>>> f = ContactForm()
>>> print(f["subject"].name)
subject
>>> print(f["message"].name)
message
BoundField.template_name [исходный код]

Имя шаблона, отображаемого с помощью BoundField.as_field_group().

Свойство возвращает значение template_name, если оно задано; в противном случае — field_template_name.

BoundField.use_fieldset [исходный код]

Возвращает значение атрибута use_fieldset виджета этого объекта BoundField.

BoundField.widget_type [исходный код]

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

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

Методы BoundField

BoundField.as_field_group()

Отображает поле с помощью BoundField.render() и значениями по умолчанию. При этом поле BoundField отображается вместе с меткой, текстом справки и ошибками с использованием шаблона template_name, если он задан; в противном случае используется field_template_name.

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.get_context() [исходный код]

Возвращает контекст шаблона для отображения поля. Доступный контекст — это field, представляющий собой экземпляр связанного поля.

BoundField.label_tag(contents=None, attrs=None, label_suffix=None, tag=None) [исходный код]

Отображает тег метки для поля формы с использованием шаблона, указанного в Form.template_name_label.

Доступный контекст:

  • field: этот экземпляр BoundField.
  • contents: по умолчанию — объединенная строка из BoundField.label и Form.label_suffix (или Field.label_suffix, если задано). Это значение можно переопределить аргументами contents и label_suffix.
  • attrs: dict, содержащий for, Form.required_css_class и id. id формируется виджетом поля attrs или свойством BoundField.auto_id. Дополнительные атрибуты можно передать аргументом attrs.
  • use_tag: логическое значение, равное True, если у метки есть id. Если значение False, шаблон по умолчанию опускает tag.
  • tag: необязательная строка для настройки тега; по умолчанию используется label.

Совет

В вашем шаблоне field — это экземпляр BoundField. Поэтому field.field обращается к BoundField.field, то есть к объявленному вами полю, например forms.CharField.

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

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

Чтобы настроить отображение, можно переопределить атрибут Form.template_name_label или, в более общем случае, шаблон по умолчанию. См. также раздел Переопределение встроенных шаблонов форм.

BoundField.legend_tag(contents=None, attrs=None, label_suffix=None) [исходный код]

Вызывает label_tag() с tag='legend', чтобы отобразить метку с тегами <legend>. Это полезно при отображении виджетов с переключателями и несколькими флажками, когда <legend> может быть уместнее, чем <label>.

BoundField.render(template_name=None, context=None, renderer=None)

Метод render вызывается методом as_field_group. Все аргументы необязательны; по умолчанию используются следующие значения:

  • template_name: BoundField.template_name
  • context: значение, возвращаемое методом BoundField.get_context()
  • renderer: значение, возвращаемое методом Form.default_renderer

Передав template_name, можно настроить шаблон для одного конкретного вызова.

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

Form.bound_field_class
Добавлено в Django 5.2.

Определите пользовательский класс BoundField, который будет использоваться при отображении формы. Он имеет приоритет над BaseRenderer.bound_field_class на уровне проекта (вместе с пользовательским FORM_RENDERER), но может быть переопределен на уровне поля с помощью Field.bound_field_class.

Если bound_field_class не определен как переменная класса, его можно задать с помощью аргумента bound_field_class в конструкторе Form или Field.

Для обеспечения совместимости пользовательское поле формы по-прежнему может переопределять Field.get_bound_field() для использования пользовательского класса, однако предпочтительнее любой из описанных выше вариантов.

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

Например, если у вас есть 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):
    bound_field_class = GPSCoordinatesBoundField

Теперь в шаблоне можно получить доступ к стране с помощью {{ form.coordinates.country }}.

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

class StyledLabelBoundField(BoundField):
    def label_tag(self, contents=None, attrs=None, label_suffix=None, tag=None):
        attrs = attrs or {}
        attrs["class"] = "wide"
        return super().label_tag(contents, attrs, label_suffix, tag)


class UserForm(forms.Form):
    bound_field_class = StyledLabelBoundField
    name = CharField()

Это изменит отображение формы по умолчанию:

>>> f = UserForm()
>>> print(f["name"].label_tag)
<label for="id_name" class="wide">Name:</label>

Чтобы добавить CSS-класс к оборачивающему HTML-элементу всех полей, можно переопределить BoundField, возвращающий другой набор CSS-классов:

class WrappedBoundField(BoundField):
    def css_classes(self, extra_classes=None):
        parent_css_classes = super().css_classes(extra_classes)
        return f"field-class {parent_css_classes}".strip()


class UserForm(forms.Form):
    bound_field_class = WrappedBoundField
    name = CharField()

После этого форма будет отображаться следующим образом:

>>> f = UserForm()
>>> print(f)
<div class="field-class"><label for="id_name">Name:</label><input type="text" name="name" required id="id_name"></div>

Чтобы переопределить класс BoundField на уровне проекта, можно определить BaseRenderer.bound_field_class в пользовательском FORM_RENDERER:

mysite/renderers.py
from django.forms.renderers import DjangoTemplates

from .forms import CustomBoundField


class CustomRenderer(DjangoTemplates):
    bound_field_class = CustomBoundField
settings.py
FORM_RENDERER = "mysite.renderers.CustomRenderer"

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

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

Проверка форм с составными данными

Form.is_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)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>

Можно создать подкласс нескольких форм, используя их как примеси. В этом примере 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)
<div>First name:<input type="text" name="first_name" required></div>
<div>Last name:<input type="text" name="last_name" required></div>
<div>Instrument:<input type="text" name="instrument" required></div>
<div>Haircut type:<input type="text" name="haircut_type" required></div>

Унаследованное от родительского класса поле 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

В один тег <form> можно поместить несколько форм Django. Чтобы задать каждой Form собственное пространство имен, используйте именованный аргумент prefix:

>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>

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

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

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

Spec-Zone.ru

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