Spec-Zone.ru › Django 2.2

API форм

О документе

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

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

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

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

Чтобы создать несвязанный экземпляр Form, просто создайте экземпляр класса:

>>> f = ContactForm()

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

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

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

Form.is_bound

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

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

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

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

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

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

Form.clean()

Реализуйте метод clean() в вашем классе Form, когда вам нужно добавить пользовательскую валидацию для полей, которые взаимозависимы. Смотрите пример использования в Очистка и валидация полей, зависящих друг от друга.

Form.is_valid()

Основная задача объекта Form — валидация данных. Для связанного экземпляра Form вызовите метод is_valid(), чтобы запустить валидацию и вернуть булево значение, указывающее, были ли данные валидны:

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

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

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
Form.errors

Получите доступ к атрибуту errors, чтобы получить словарь сообщений об ошибках:

>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

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

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

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

Form.errors.as_data()

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

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

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

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

Form.has_changed()

Используйте метод has_changed() для вашей Form формы, чтобы проверить, были ли данные формы изменены со времени первоначальных данных.

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

При отправке формы она пересоздаётся и предоставляются исходные данные, чтобы можно было провести сравнение:

>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()

has_changed() будет True, если данные из request.POST отличаются от тех, что были предоставлены в initial, или False в противном случае. Результат вычисляется путём вызова Field.has_changed() для каждого поля формы.

Form.changed_data

Атрибут changed_data возвращает список имён полей, значения которых в связанных данных формы (обычно request.POST) отличаются от предоставленных в initial. Возвращает пустой список, если данные не отличаются.

>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
...     print("The following fields changed: %s" % ", ".join(f.changed_data))
>>> f.changed_data
['subject', 'message']

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

Form.fields

Вы можете получить доступ к полям экземпляра Form из его атрибута fields.

>>> for row in f.fields.values(): print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields['name']
<django.forms.fields.CharField object at 0x7ffaac6324d0>

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

>>> f.as_table().split('\n')[0]
'<tr><th>Name:</th><td><input name="name" type="text" value="instance" required></td></tr>'
>>> f.fields['name'].label = "Username"
>>> f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="instance" required></td></tr>'

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

>>> f.base_fields['name'].label = "Username"
>>> another_f = CommentForm(auto_id=False)
>>> another_f.as_table().split('\n')[0]
'<tr><th>Username:</th><td><input name="name" type="text" value="class" required></td></tr>'

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

Form.cleaned_data

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

Например, DateField приводит входные данные к объекту Python datetime.date. Независимо от того, передаёте ли вы строку в формате '1994-07-15', объект datetime.date или другие форматы, DateField всегда приведёт их к объекту datetime.date при условии, что он корректный.

После создания экземпляра Form с набором данных и его валидации, вы можете получить доступ к очищенным данным через его атрибут cleaned_data.

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

Обратите внимание, что любые текстовые поля – такие как CharField или EmailField – всегда очищают ввод в строку. Мы рассмотрим последствия кодирования позже в этом документе.

Если ваши данные не прошли валидацию, словарь cleaned_data содержит только допустимые поля:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}

cleaned_data всегда только содержит ключ для полей, определённых в Form, даже если вы передаёте дополнительные данные при определении Form. В этом примере мы передаём множество дополнительных полей конструктору ContactForm, но cleaned_data содержит только поля формы:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True,
...         'extra_field_1': 'foo',
...         'extra_field_2': 'bar',
...         'extra_field_3': 'baz'}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

Когда Form валиден, cleaned_data будет содержать ключ и значение для всех его полей, даже если данные не содержали значения для некоторых необязательных полей. В этом примере словарь данных не содержит значения для поля nick_name, но cleaned_data включает его со значением по умолчанию:

>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
...     nick_name = forms.CharField(required=False)
>>> data = {'first_name': 'John', 'last_name': 'Lennon'}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}

В этом примере значение cleaned_data для nick_name установлено в пустую строку, потому что nick_name является CharField, а CharField обрабатывают пустые значения как пустую строку. Каждый тип поля знает, каково его "пустое" значение – например, для DateField это None, а не пустая строка. Более подробную информацию о поведении каждого поля в этом случае см. в примечании "Пустое значение" для каждого поля в разделе "Встроенные Field классы" ниже.

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

Вывод форм в HTML

Вторая задача объекта Form – отобразить себя в HTML. Для этого просто print его:

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

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

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

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

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

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

as_p()

Form.as_p()

as_p() отображает форму в виде набора тегов <p>, где каждый <p> содержит одно поле:

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

as_ul()

Form.as_ul()

as_ul() отображает форму в виде ряда тегов <li>, где каждый тег <li> содержит одно поле. Он не включает <ul> или </ul>, чтобы вы могли указать любые HTML-атрибуты на <ul> для гибкости:

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

as_table()

Form.as_table()

Наконец, as_table() выводит форму как HTML-таблицу <table>. Это точно так же, как print. На самом деле, при обращении к объекту формы он вызывает его метод as_table() в фоновом режиме:

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

Стилевые настройки или ошибки строк формы

Form.error_css_class
Form.required_css_class

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

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

from django import forms

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

    # ... and the rest of your fields here

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

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

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

Form.auto_id

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

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

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

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

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

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

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

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

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

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

Если auto_id установлено в любое другое истинное значение — например, в строку, которая не включает %s — библиотека будет действовать так, как если бы auto_id было True.

По умолчанию auto_id установлено в строку 'id_%s'.

Form.label_suffix

Переводной строкой (по умолчанию двоеточие (:) на английском языке), которая добавляется после имени любой метки при рендеринге формы.

Можно настроить этот символ или полностью его опустить, используя параметр label_suffix:

>>> f = ContactForm(auto_id='id_for_%s', label_suffix='')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>
>>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" required></li>
<li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" required></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself"></li>

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

Поля также могут определять свой собственный label_suffix. Это значение будет иметь приоритет над Form.label_suffix. Суффикс также может быть переопределен во время выполнения, используя параметр label_suffix к label_tag().

Form.use_required_attribute

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

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

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

Form.default_renderer

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

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

from django import forms

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

или:

form = MyForm(renderer=MyRenderer())

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

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

Существует несколько других способов настройки порядка:

Form.field_order

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

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

Form.order_fields(field_order)

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

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

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

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

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

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

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

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

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

class BoundField [source]

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

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

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

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

Чтобы получить все объекты BoundField, пройдитесь по форме:

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

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

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

Атрибуты BoundField

BoundField.auto_id

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

BoundField.data

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

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

Объект, подобный списку, отображаемый как элемент HTML <ul class="errorlist"> при выводе:

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

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

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

BoundField.form

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

BoundField.help_text

Описание поля help_text.

BoundField.html_name

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

BoundField.id_for_label

Используйте это свойство для рендеринга ID этого поля. Например, если вы вручную генерируете <label> в шаблоне (хотя label_tag() сделает это за вас):

<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}

По умолчанию это будет имя поля с префиксом id_ («id_my_field» для примера выше). Вы можете изменить ID, установив attrs на виджете поля. Например, объявление поля так:

my_field = forms.CharField(widget=forms.TextInput(attrs={'id': 'myFIELD'}))

и использование указанного выше шаблона, отобразит что-то вроде:

<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
BoundField.is_hidden

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

BoundField.label

Метка поля label. Используется в label_tag().

BoundField.name

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

>>> f = ContactForm()
>>> print(f['subject'].name)
subject
>>> print(f['message'].name)
message

Методы BoundField

BoundField.as_hidden(attrs=None, **kwargs) [source]

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

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

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

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

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

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

BoundField.css_classes() [source]

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

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes()
'required'

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

>>> f = ContactForm(data={'message': ''})
>>> f['message'].css_classes('foo bar')
'foo bar required'
BoundField.label_tag(contents=None, attrs=None, label_suffix=None) [source]

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

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

Вы можете указать параметр contents, который заменит автоматически сгенерированный тег метки. Словарь attrs может содержать дополнительные атрибуты для тега <label>.

Сгенерированный HTML включает суффикс метки формы label_suffix (двоеточие по умолчанию) или, если задано, текущий суффикс метки поля label_suffix. Дополнительный параметр label_suffix позволяет переопределить любой ранее заданный суффикс. Например, вы можете использовать пустую строку, чтобы скрыть метку для выбранных полей. Если вам нужно сделать это в шаблоне, вы можете написать пользовательский фильтр для передачи параметров в метод label_tag.

BoundField.value() [source]

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

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

Настройка BoundField

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

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

Field.get_bound_field(form, field_name) [source]

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

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

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

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

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

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

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

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

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

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

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

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

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

Создание свободной формы – это как всегда – просто опустите и данные формы, и данные файла:

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

Проверка на multipart формы

Form.is_multipart()

Если вы пишете повторно используемые представления или шаблоны, вам может быть неизвестно заранее, является ли ваша форма multipart формой или нет. Метод is_multipart() сообщает вам, требует ли форма multipart кодирование для отправки:

>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True

Вот пример того, как вы можете использовать это в шаблоне:

{% if form.is_multipart %}
    <form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
    <form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>

Наследование форм

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

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

В этом примере, ContactFormWithPriority содержит все поля из ContactForm, плюс дополнительное поле, priority. Поля ContactForm упорядочены первыми:

>>> class ContactFormWithPriority(ContactForm):
...     priority = forms.CharField()
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" required></li>
<li>Message: <input type="text" name="message" required></li>
<li>Sender: <input type="email" name="sender" required></li>
<li>Cc myself: <input type="checkbox" name="cc_myself"></li>
<li>Priority: <input type="text" name="priority" required></li>

Можно наследовать от нескольких форм, рассматривая формы как миксины. В этом примере, BeatleForm наследуется от PersonForm и InstrumentForm (в этом порядке), и его список полей включает поля из родительских классов:

>>> from django import forms
>>> class PersonForm(forms.Form):
...     first_name = forms.CharField()
...     last_name = forms.CharField()
>>> class InstrumentForm(forms.Form):
...     instrument = forms.CharField()
>>> class BeatleForm(InstrumentForm, PersonForm):
...     haircut_type = forms.CharField()
>>> b = BeatleForm(auto_id=False)
>>> print(b.as_ul())
<li>First name: <input type="text" name="first_name" required></li>
<li>Last name: <input type="text" name="last_name" required></li>
<li>Instrument: <input type="text" name="instrument" required></li>
<li>Haircut type: <input type="text" name="haircut_type" required></li>

Возможно явно удалить Field поле, унаследованное от родительского класса, установив имя поля в None в подклассе. Например:

>>> from django import forms

>>> class ParentForm(forms.Form):
...     name = forms.CharField()
...     age = forms.IntegerField()

>>> class ChildForm(ParentForm):
...     name = None

>>> list(ChildForm().fields)
['age']

Префиксы для форм

Form.prefix

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

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

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

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

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

Spec-Zone.ru

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