API форм

Об этом документе

В этом документе рассматриваются детали API форм Django. Сначала вам следует прочитать introduction to working with forms.

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

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

  • Если он связан с набором данных, он способен проверить эти данные и вывести форму в виде HTML с отображением данных в HTML.
  • Если это unbound, он не может выполнить валидацию (потому что нет данных для валидации!), но он все еще может отобразить пустую форму как 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 или с помощью функции 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)

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

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

Form.non_field_errors()

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

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

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

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

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

Form.initial

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

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

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

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

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

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial='class')
...     url = forms.URLField()
...     comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required></td></tr>
Form.get_initial_for_field(field, field_name)

Возвращает исходные данные для поля формы. Он извлекает данные из Form.initial, если они присутствуют, в противном случае пробует Field.initial. Вызываемые значения оцениваются.

Рекомендуется использовать BoundField.initial вместо get_initial_for_field(), поскольку BoundField.initial имеет более простой интерфейс. Кроме того, в отличие от get_initial_for_field(), BoundField.initial кэширует свои значения. Это особенно полезно при работе с callables, возвращаемые значения которых могут меняться (например, 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>

You can alter the field and BoundField of Form instance to change the way it is presented in the 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 = CommentForm(auto_id=False)
>>> 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 вместо пустой строки. Для получения подробной информации о поведении каждого поля в этом случае, смотрите примечание «Пустое значение» для каждого поля в разделе «Встроенные классы Field» ниже.

Вы можете написать код для выполнения валидации для отдельных полей формы (на основе их названия) или для формы в целом (учитывая комбинации различных полей). Более подробная информация об этом содержится в Валидация форм и полей.

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

Вторая задача объекта Form - отобразить себя в виде HTML. Чтобы сделать это, print его:

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

Если форма связана с данными, то HTML-вывод будет включать эти данные соответствующим образом. Например, если поле представлено символом <input type="text">, то данные будут находиться в атрибуте value. Если поле представлено символом <input type="checkbox">, то HTML-вывод будет включать checked, если это уместно:

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

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

  • Для гибкости вывод не включает теги <table> и </table>, а также теги <form> и </form> или тег <input type="submit">. Это ваша работа, чтобы сделать это.
  • Каждый тип поля имеет HTML-представление по умолчанию. CharField представляется символом <input type="text">, а EmailField - <input type="email">. BooleanField(null=False) представляется символом <input type="checkbox">. Обратите внимание, что это всего лишь разумные значения по умолчанию; вы можете указать, какой HTML использовать для данного поля, используя виджеты, о которых мы расскажем в ближайшее время.
  • HTML name для каждого тега берется непосредственно из имени его атрибута в классе ContactForm.
  • Текстовая метка для каждого поля - например, 'Subject:', 'Message:' и 'Cc myself:' - генерируется из имени поля путем преобразования всех знаков подчеркивания в пробелы и перевода первой буквы в верхний регистр. Еще раз отметим, что это всего лишь разумные значения по умолчанию; вы также можете задать метки вручную.
  • Каждая текстовая метка окружена тегом HTML <label>, который указывает на соответствующее поле формы через его id. Его id, в свою очередь, формируется путем добавления 'id_' к имени поля. Атрибуты id и теги <label> включаются в вывод по умолчанию, чтобы следовать лучшим практикам, но вы можете изменить это поведение.
  • При выводе используется синтаксис HTML5, ориентированный на <!DOCTYPE html>. Например, он использует булевы атрибуты, такие как checked, а не стиль XHTML checked='checked'.

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

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

Рендеринг по умолчанию при print форме использует следующие методы и атрибуты.

template_name

New in Django 4.0.
Form.template_name

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

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

Changed in Django 4.1:

В старых версиях template_name по умолчанию использовалось строковое значение 'django/forms/default.html'.

render()

New in Django 4.0.
Form.render(template_name=None, context=None, renderer=None)

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

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

get_context()

New in Django 4.0.
Form.get_context()

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

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

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

template_name_label

New in Django 4.0.
Form.template_name_label

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

Стили вывода

Помимо непосредственного отображения формы, например, в шаблоне с {{ form }}, следующие вспомогательные функции служат в качестве прокси для Form.render(), передавая определенное значение template_name.

Эти помощники наиболее полезны в шаблоне, когда вам нужно переопределить рендерер формы или значение, предоставляемое формой, но вы не можете передать дополнительный параметр в render(). Например, вы можете отобразить форму в виде неупорядоченного списка с помощью {{ form.as_ul }}.

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

as_div()

Form.template_name_div
New in Django 4.1.

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

Form.as_div()
New in Django 4.1.

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_div(), а не as_p(), as_table() и as_ul(), поскольку в этом шаблоне реализованы <fieldset> и <legend> для группировки связанных вводимых данных, и в нем легче ориентироваться пользователям программы чтения с экрана.

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)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>

Стилизация требуемых или ошибочных строк формы

Form.error_css_class
Form.required_css_class

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

Класс Form имеет пару крючков, которые можно использовать для добавления атрибутов class к необходимым строкам или к строкам с ошибками: установите атрибуты Form.error_css_class и/или Form.required_css_class:

from django import forms

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

    # ... and the rest of your fields here

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

>>> f = ContactForm(data)
>>> print(f.as_table())
<tr class="required"><th><label class="required" for="id_subject">Subject:</label>    ...
<tr class="required"><th><label class="required" for="id_message">Message:</label>    ...
<tr class="required error"><th><label class="required" for="id_sender">Sender:</label>      ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...
>>> f['subject'].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f['subject'].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>

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

Form.auto_id

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

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

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

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

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

>>> f = ContactForm(auto_id=False)
>>> print(f.as_div())
<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.as_div())
<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.as_div())
<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.as_div())
<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.as_div())
<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 -&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.

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

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

Form.default_renderer

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

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

from django import forms

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

или:

form = MyForm(renderer=MyRenderer())

Примечания по заказу полевых работ

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

Существует еще несколько способов индивидуализации заказа:

Form.field_order

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

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

Form.order_fields(field_order)

Вы можете переставлять поля в любое время, используя order_fields() со списком имен полей, как в field_order.

Как отображаются ошибки

Если вы выводите связанный объект Form, то при выводе автоматически выполняется валидация формы, если она еще не выполнена, и в HTML-вывод включаются ошибки валидации в виде <ul class="errorlist"> рядом с полем. Конкретное расположение сообщений об ошибках зависит от используемого метода вывода:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data, auto_id=False)
>>> print(f.as_div())
<div>Subject:<ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required>Hi there</textarea></div>
<div>Sender:<ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself" checked></div>
>>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required></td></tr>
<tr><th>Message:</th><td><textarea name="message" cols="40" rows="10" required></textarea></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required></td></tr>
<tr><th>Cc myself:</th><td><input checked type="checkbox" name="cc_myself"></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <textarea name="message" cols="40" rows="10" required></textarea></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required></li>
<li>Cc myself: <input checked type="checkbox" name="cc_myself"></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <textarea name="message" cols="40" rows="10" required></textarea></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>

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

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

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

error_class

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

renderer
New in Django 4.0.

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

template_name
New in Django 4.0.

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

template_name_text
New in Django 4.0.

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

template_name_ul
New in Django 4.0.

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

get_context()
New in Django 4.0.

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

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

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

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

Changed in Django 4.0:

Рендеринг ErrorList был перенесен в шаблонизатор.

Не рекомендуется, начиная с версии 4.0: Возможность возвращать str при вызове метода __str__ устарела. Вместо этого используйте шаблонизатор, который возвращает SafeString.

Более детальный вывод

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

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

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

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

Чтобы получить одно BoundField, используйте синтаксис поиска по словарю в вашей форме, используя имя поля в качестве ключа:

>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" required>

Чтобы получить все объекты BoundField, выполните итерацию формы:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">

Вывод для конкретного поля соответствует настройке auto_id объекта формы:

>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" required>

Атрибуты BoundField

BoundField.auto_id

Атрибут HTML ID для данного BoundField. Возвращает пустую строку, если Form.auto_id является False.

BoundField.data

Это свойство возвращает данные для этого BoundField, извлеченные методом value_from_datadict() виджета, или None, если они не были переданы:

>>> unbound_form = ContactForm()
>>> print(unbound_form['subject'].data)
None
>>> bound_form = ContactForm(data={'subject': 'My Subject'})
>>> print(bound_form['subject'].data)
My Subject
BoundField.errors

list-like object, который при печати отображается как HTML <ul class="errorlist">:

>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f['message'].errors
['This field is required.']
>>> print(f['message'].errors)
<ul class="errorlist"><li>This field is required.</li></ul>
>>> f['subject'].errors
[]
>>> print(f['subject'].errors)

>>> str(f['subject'].errors)
''
BoundField.field

Экземпляр формы Field из класса формы, в который обернут этот BoundField.

BoundField.form

Экземпляр Form, к которому привязан данный BoundField.

BoundField.help_text

Значение help_text поля.

BoundField.html_name

Имя, которое будет использоваться в HTML атрибуте виджета name. При этом учитывается форма prefix.

BoundField.id_for_label

Используйте это свойство для отображения ID этого поля. Например, если вы вручную создаете <label> в своем шаблоне (несмотря на то, что label_tag()/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 кэширует свое возвращаемое значение, что особенно полезно при работе с callables, возвращаемые значения которых могут изменяться (например, 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.use_fieldset
New in Django 4.1.

Возвращает значение атрибута 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_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'

Если вы хотите предоставить некоторые дополнительные классы в дополнение к классам error и required, которые могут потребоваться, вы можете предоставить эти классы в качестве аргумента:

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes('foo bar')
'foo bar required'
BoundField.label_tag(contents=None, attrs=None, label_suffix=None, tag=None)[исходный код]

Отображает тег label для поля формы, используя шаблон, указанный 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 поля формы, можно вызвать его метод label_tag():

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

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

Changed in Django 4.0:

Теперь этикетка отображается с помощью шаблонизатора.

Changed in Django 4.1:

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

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

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

BoundField.value()[исходный код]

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

>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data={'subject': 'hi'}, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi

Настройка BoundField

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

Поле пользовательской формы может переопределять get_bound_field():

Field.get_bound_field(form, field_name)[исходный код]

Принимает экземпляр Form и имя поля. Возвращаемое значение будет использоваться при обращении к полю в шаблоне. Скорее всего, это будет экземпляр подкласса BoundField.

Если у вас есть, например, GPSCoordinatesField и вы хотите иметь возможность доступа к дополнительной информации о координатах в шаблоне, это можно реализовать следующим образом:

class GPSCoordinatesBoundField(BoundField):
    @property
    def country(self):
        """
        Return the country the coordinates lie in or None if it can't be
        determined.
        """
        value = self.value()
        if value:
            return get_country_from_coordinates(value)
        else:
            return None

class GPSCoordinatesField(Field):
    def get_bound_field(self, form, field_name):
        return GPSCoordinatesBoundField(form, self, field_name)

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

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

Работа с формами, имеющими поля FileField и ImageField, немного сложнее, чем с обычной формой.

Во-первых, чтобы загрузить файлы, вам нужно убедиться, что ваш элемент <form> правильно определяет enctype как "multipart/form-data":

<form enctype="multipart/form-data" method="post" action="/foo/">

Во-вторых, когда вы используете форму, вам необходимо связать данные файла. Файловые данные обрабатываются отдельно от обычных данных формы, поэтому, если ваша форма содержит FileField и ImageField, вам необходимо указать второй аргумент при привязке формы. Таким образом, если мы расширим нашу ContactForm и добавим в нее ImageField, называемую mugshot, нам нужно будет связать данные файла, содержащего изображение кружки:

# Bound form with an image field
>>> from django.core.files.uploadedfile import SimpleUploadedFile
>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> file_data = {'mugshot': SimpleUploadedFile('face.jpg', <file data>)}
>>> f = ContactFormWithMugshot(data, file_data)

На практике вы обычно указываете request.FILES в качестве источника данных файла (точно так же, как вы используете request.POST в качестве источника данных формы):

# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)

Конструирование несвязанной формы происходит так же, как и всегда - опустите данные формы и данные файла:

# Unbound form with an image field
>>> f = ContactFormWithMugshot()

Тестирование многочастных форм

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.as_div())
<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.as_div())
<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

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

>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother.as_div())
<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.as_div())
<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:

>>> class PersonForm(forms.Form):
...     ...
...     prefix = 'person'
Вернуться на верх