• 3.1
  • 3.2
  • 5.0
  • Версия документации: 6.1

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

Давайте попробуем с некоторыми неверными данными. В этом случае тема пуста (ошибка, поскольку все поля обязательны по умолчанию), а отправитель не является действительным адресом электронной почты:

>>> 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 можно без предварительного вызова методе call 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.'])]}

Используйте этот метод каждый раз, когда вам нужно идентифицировать ошибку по ее «коду». Это позволяет выполнять такие действия, как перезапись сообщения об ошибке или написание собственной логики в представлении при наличии данной ошибки. Его также можно использовать для сериализации ошибок в специальном формате (например, XML); например, as_json() использует as_data().

Метод as_data() добавлен для обратной совместимости. В предыдущих версиях объекты ValidationError терялись при получении сообщения с ошибкой и добавления его в словарь Form.errors. В идеале Form.errors должен содержать объекты ValidationError и метод с префиксом as_ должен преобразовать их в текстовые сообщения, но нам пришлось пойти другим путем, чтобы не сломать существующий код, который ожидает получить сообщения с ошибками из Form.errors.

Form.errors.as_json(escape_html=False)

Возвращает ошибки в JSON формате.

>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}

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

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

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

Этот метод возвращает True, если поле содержит ошибку с кодом code. Если code равен None, вернет True, если поле содержит любую ошибку.

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

Form.non_field_errors()

Этот метод возвращает список ошибок из Form.errors, которые не привязаны к конкретному полю. Сюда входят ошибки ValidationError, вызванные в Form.clean(), и ошибки, добавленные через Form.add_error(None, "...").

Поведение незаполненных форм

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

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

Начальные значения формы

Form.initial

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

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

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

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

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

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial="class")
...     url = forms.URLField()
...     comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<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(), если необходимо проверить были ли изначальные данные изменены.

>>> 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 нормализует введённое значение к объекту 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 всегда только будет содержать ключ для полей, определенных в Форма, даже если вы передаете дополнительные данные при определении Формы. В этом примере мы передаем несколько дополнительных полей в конструктор 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'}

Когда «Форма» действительна, «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. Для этого распечатайте его:

>>> 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 использовать для данного поля, с помощью виджетов, которые мы вскоре объясним.

  • Атрибут name каждого тега совпадает напрямую с именем атрибута в классе ContactForm.

  • Текстовая метка каждого поля, т.е. 'Subject:', 'Message:' и 'Cc myself:', генерируется из имени поля, конвертируя символы подчёркивания в пробелы и переводя первый символ в верхний регистр. Также, вы можете явно назначить текстовую метку для поля.

  • Каждая текстовая метка выводится с помощью тега <label>, который указывает на соответствующее поле формы с помощью атрибута id. Атрибут id генерируется путём добавления префикса 'id_' к имени поля. Атрибуты id и теги <label> включаются в HTML представление формы по умолчанию, но можете изменить такое поведение формы.

  • В выводе используется синтаксис HTML5 с таргетингом на <!DOCTYPE html>. Например, он использует логические атрибуты, такие как checked, а не стиль XHTML checked='checked'.

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

Рендеринг по умолчанию

При рендеринге по умолчанию, когда вы «печатаете» форму, используются следующие методы и атрибуты.

имя_шаблона

Form.template_name

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

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

рендер()

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

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

  • имя_шаблона: Form.имя_шаблона

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

  • renderer: значение, возвращаемое Form.default_renderer

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

get_context()

Form.get_context()

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

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

  • form: связанная форма.

  • fields: все связанные поля, кроме скрытых.

  • hidden_fields: все скрытые связанные поля.

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

имя_шаблона_метка

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

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>

Вы можете дополнительно изменить отображение строк формы, используя custom BoundField.

Настройка тегов <label> и атрибута id

Form.auto_id

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

  • HTML атрибуты id элементов формы.

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

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

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

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

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

Есть и другие способы переопределить порядок полей в форме:

Form.field_order

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

Также можно передать аргумент 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,
... }
>>> 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>).

Changed in Django 5.2:

aria-describedby была добавлена, чтобы связать ошибки с входными данными.

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

class ErrorList(initlist=None, error_class=None, renderer=None, field_id=None)

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

Changed in Django 5.2:

Был добавлен аргумент field_id.

error_class

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

renderer

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

field_id
New in Django 5.2.

Идентификатор поля, к которому относятся ошибки. Это позволяет добавить атрибут 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)

Метод рендеринга вызывается __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__() (__unicode__ в Python 2) этого объекта возвращают 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
New in Django 5.2.

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

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

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

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

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.

Совет

В вашем шаблоне «поле» является экземпляром «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)

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

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

BoundField.value()

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

>>> 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
New in 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, вам нужно будет указать второй аргумент при привязке формы. Итак, если мы расширим нашу контактную форму, включив в нее ImageField под названием 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() сообщает вам, требует ли форма многочастного кодирования для отправки:

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

Можно декларативно удалить «Поле», унаследованное от родительского класса, установив для имени поля значение «Нет» в подклассе. Например:

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