API форм¶
Об этом документе
Этот документ знакомит с деталями API форм Django. Сначала вы должны прочитать введение в использование форм.
Заполненные и незаполненные формы¶
Экземпляр класса Form может быть заполнен набором данных или не заполнен.
Если он заполнен, то у него есть возможность валидации полученных данных и генерации заполненной формы в виде HTML кода.
Если он не заполнен, то выполнить валидацию невозможно (так как нет для этого данных!), но есть возможность сгенерировать HTML код пустой формы.
- class Form¶
To create an unbound Form instance, instantiate the class:
>>> f = ContactForm()
To bind data to a form, pass the data as a dictionary as the first parameter to
your Form class constructor:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
В этом словаре, ключами являются имена полей, которые соответствуют атрибутам в вашем классе Form. Значения словаря являются данными, которые вам требуется проверить. Обычно значения представлены строками, но это требование не является обязательным. Тип переданных данных зависит от класса Field, это мы сейчас рассмотрим.
- Form.is_bound¶
If you need to distinguish between bound and unbound form instances at runtime,
check the value of the form’s is_bound attribute:
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True
Note that passing an empty dictionary creates a bound form with empty data:
>>> f = ContactForm({})
>>> f.is_bound
True
Если есть заполненный экземпляр Form и требуется как-то изменить его данные или если требуется привязать данные к незаполненному экземпляру Form, то создаёте другой экземпляр Form. Нет способа изменить данные в экземпляре Form. После создания экземпляра Form, его данные следует рассматривать как неизменные, независимо от его заполненности.
Использование форм для проверки данных¶
- Form.clean()¶
Добавьте метод clean() в вашу форму Form, если необходимо проверить независимые поля вместе. Смотрите Очистка и проверка полей, которые зависят друг от друга.
- Form.is_valid()¶
The primary task of a Form object is to validate data. With a bound
Form instance, call the is_valid() method to run validation
and return a boolean designating whether the data was valid:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
Let’s try with some invalid data. In this case, subject is blank (an error,
because all fields are required by default) and sender is not a valid
email address:
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid email address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
- Form.errors¶
Access the errors attribute to get a dictionary of error
messages:
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
В этом словаре, ключами являются имена полей, а значениями – списки юникодных строк, представляющих сообщения об ошибках. Сообщения хранятся в виде списков, так как поле может иметь множество таких сообщений.
Обращаться к атрибуту errors можно без предварительного вызова методе call is_valid(). Данные формы будут проверены при вызове метода is_valid() или при обращении к errors.
Процедуры проверки выполняются один раз, независимо от количества обращений к атрибуту errors или вызова метода is_valid(). Это означает, что если проверка данных имеет побочное влияние на состояние формы, то оно проявится только один раз.
- Form.errors.as_data()¶
Возвращает dict, который содержит поля и объекты ValidationError.
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
Используйте этот метод, если вам необходимо определить тип ошибки по её 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)¶
Этот метод возвращает True, если поле содержит ошибку с кодом code. Если code равен None, вернет True, если поле содержит любую ошибку.
Чтобы проверить наличие ошибок, которые не относятся ни к одному из полей, передайте NON_FIELD_ERRORS в параметре field.
- Form.non_field_errors()¶
Этот метод возвращает список ошибок из Form.errors, которые не привязаны к конкретному полю. Сюда входят ошибки ValidationError, вызванные в Form.clean(), и ошибки, добавленные через Form.add_error(None, "...").
Поведение незаполненных форм¶
It’s meaningless to validate a form with no data, but, for the record, here’s what happens with unbound forms:
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Dynamic initial values¶
- Form.initial¶
Используйте атрибут initial для определения начальных значений полей формы во время работы приложения. Например, вам может потребоваться заполнить поле username именем текущего пользователя.
To accomplish this, use the initial argument to a Form.
This argument, if given, should be a dictionary mapping field names to initial
values. Only include the fields for which you’re specifying an initial value;
it’s not necessary to include every field in your form. For example:
>>> f = ContactForm(initial={'subject': 'Hi there!'})
Эти значения только отображаются на незаполненных формах, они не будут доступны пока форма не будет отправлена пользователем.
If a Field defines initial and you
include initial when instantiating the Form, then the latter
initial will have precedence. In this example, initial is provided both
at the field level and at the form instance level, and the latter gets
precedence:
>>> 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)¶
Use get_initial_for_field() to retrieve initial data for a form
field. It retrieves data from Form.initial and Field.initial,
in that order, and evaluates any callable initial values.
Проверяем какие данные формы были изменены¶
- Form.has_changed()¶
Используйте метод has_changed(), если необходимо проверить были ли изначальные данные изменены.
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
После отправки формы, мы создаем ее, предоставляя изначальные данные, и теперь может проверить изменились ли они:
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
has_changed() вернет True, если данные из request.POST отличаются от данных из initial, иначе вернет False. Результат вычисляется путем вызова Field.has_changed() для каждого поля формы.
- Form.changed_data¶
Атрибут changed_data возвращает список полей, чии значения из переданных данных (обычно request.POST) отличаются от значений из initial. Возвращает пустой список, если данные не поменялись.
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
>>> f.changed_data
['subject', 'message']
Обращение к полям из формы¶
- Form.fields¶
You can access the fields of Form instance from its fields
attribute:
>>> 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 of Form instance to change the way it is
presented in the 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>'
Beware not to alter the base_fields attribute because this modification
will influence all subsequent ContactForm instances within the same Python
process:
>>> 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 нормализует введённое значение к объекту datetime.date. Независимо от того, передали ли вы строку в формате '1994-07-15', объект datetime.date или число в других форматах, DateField всегда преобразует его в объект datetime.date, если при этом не произойдёт ошибка.
Once you’ve created a Form instance with a set of data and validated
it, you can access the clean data via its cleaned_data attribute:
>>> 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, всегда преобразует текст в юникодную строку. Мы рассмотрим применения кодировок далее.
If your data does not validate, the cleaned_data dictionary contains
only the valid fields:
>>> 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 will always only contain a key for fields defined in the
Form, even if you pass extra data when you define the Form. In this
example, we pass a bunch of extra fields to the ContactForm constructor,
but cleaned_data contains only the form’s fields:
>>> 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'}
When the Form is valid, cleaned_data will include a key and value for
all its fields, even if the data didn’t include a value for some optional
fields. In this example, the data dictionary doesn’t include a value for the
nick_name field, but cleaned_data includes it, with an empty value:
>>> 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, на не пустая строка. Для получения подробностей о поведении каждого типа поля обращайте внимание на заметку «Пустое значение» для каждого поля в разделе «Встроенные поля», которые приведён далее.
Вы можете написать код для выполнения проверки определённых полей формы (используя их имена) или для проверки всей формы (рассматривая её как комбинацию полей). Подробная информация изложена в Проверка форм и полей формы.
Выдача формы в виде HTML¶
The second task of a Form object is to render itself as HTML. To do so,
print it:
>>> 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>
If the form is bound to data, the HTML output will include that data
appropriately. For example, if a field is represented by an
<input type="text">, the data will be in the value attribute. If a
field is represented by an <input type="checkbox">, then that HTML will
include checked if appropriate:
>>> 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>
This default output is a two-column HTML table, with a <tr> for each field.
Notice the following:
For flexibility, the output does not include the
<table>and</table>tags, nor does it include the<form>and</form>tags or an<input type="submit">tag. It’s your job to do that.Каждый тип поля имеет представление HTML по умолчанию.
CharFieldпредставлен<input type="text">иEmailFieldкак<input type="email">.BooleanField(null=False)представлен<input type="checkbox">. Обратите внимание, что это всего лишь разумные значения по умолчанию; вы можете указать, какой HTML использовать для данного поля, с помощью виджетов, которые мы вскоре объясним.Атрибут
nameкаждого тега совпадает напрямую с именем атрибута в классеContactForm.Текстовая метка каждого поля, т.е.
'Subject:','Message:'и'Cc myself:', генерируется из имени поля, конвертируя символы подчёркивания в пробелы и переводя первый символ в верхний регистр. Также, вы можете явно назначить текстовую метку для поля.Каждая текстовая метка выводится с помощью тега
<label>, который указывает на соответствующее поле формы с помощью атрибутаid. Атрибутidгенерируется путём добавления префикса'id_'к имени поля. Атрибутыidи теги<label>включаются в HTML представление формы по умолчанию, но можете изменить такое поведение формы.В выводе используется синтаксис HTML5 с таргетингом на
<!DOCTYPE html>. Например, он использует логические атрибуты, такие какchecked, а не стиль XHTMLchecked='checked'.
Although <table> output is the default output style when you print a
form, other output styles are available. Each style is available as a method on
a form object, and each rendering method returns a string.
as_p()¶
- Form.as_p()¶
as_p() renders the form as a series of <p> tags, with each <p>
containing one field:
>>> 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() renders the form as a series of <li> tags, with each
<li> containing one field. It does not include the <ul> or
</ul>, so that you can specify any HTML attributes on the <ul> for
flexibility:
>>> 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()¶
Finally, as_table() outputs the form as an HTML <table>. This is
exactly the same as print. In fact, when you print a form object,
it calls its as_table() method behind the scenes:
>>> 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 attributes:
from django import forms
class ContactForm(forms.Form):
error_css_class = 'error'
required_css_class = 'required'
# ... and the rest of your fields here
Once you’ve done that, rows will be given "error" and/or "required"
classes, as needed. The HTML will look something like:
>>> 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>
Настройка отрисовки виджетов формы¶
- 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. Для изменения их порядка на форме, просто поменяйте их местами в классе.
Есть и другие способы переопределить порядок полей в форме:
- Form.field_order¶
По умолчанию Form.field_order=None, при этом поля формы будут в порядке их определения. Если field_order содержит список названий полей формы, поля будут выведены в указанном порядке, а оставшиеся – в порядке по умолчанию. Неизвестные названия полей будут проигнорированы. Это позволяете удалить поле в классе-наследние, установив его в None, и при это вам не нужно переопределять field_order.
Также можно передать аргумент Form.field_order в Form, чтобы переопределить порядок полей. Если Form содержит field_order и и передать аргумент field_order при создании Form, будет использоваться последний field_order.
- Form.order_fields(field_order)¶
Вы можете изменить порядок полей, используя метод order_fields() и передав в него список полей, как и для field_order.
Отображение ошибок¶
If you render a bound Form object, the act of rendering will automatically
run the form’s validation if it hasn’t already happened, and the HTML output
will include the validation errors as a <ul class="errorlist"> near the
field. The particular positioning of the error messages depends on the output
method you’re using:
>>> 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>
Настройка формата списка ошибок¶
By default, forms use django.forms.utils.ErrorList to format validation
errors. If you’d like to use an alternate class for displaying errors, you can
pass that in at construction time:
>>> from django.forms.utils import ErrorList
>>> class DivErrorList(ErrorList):
... def __str__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return ''
... return '<div class="errorlist">%s</div>' % ''.join(['<div class="error">%s</div>' % e for e in self])
>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
>>> f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" required></p>
<p>Message: <input type="text" name="message" value="Hi there" required></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="email" name="sender" value="invalid email address" required></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself"></p>
Точное управление выводом¶
Методы as_p(), as_ul() и as_table() являются упрошенными методами отображения формы. Это не единственные способы отображения формы.
- class BoundField¶
Используется для отображения HTML или для доступа к атрибутам отдельного поля экземпляра
Form.Метод
__str__()(__unicode__в Python 2) этого объекта возвращают HTML для данного поля.
To retrieve a single BoundField, use dictionary lookup syntax on your form
using the field’s name as the key:
>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" required>
To retrieve all BoundField objects, iterate the form:
>>> 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">
The field-specific output honors the form object’s auto_id setting:
>>> 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¶
This property returns the data for this
BoundFieldextracted by the widget’svalue_from_datadict()method, orNoneif it wasn’t given:>>> 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¶
A list-like object that is displayed as an HTML
<ul class="errorlist">when printed:>>> 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.html_name¶
Название, которое будет использоваться в HTML атрибуте
nameвиджета. Учитываетprefixформы.
- BoundField.id_for_label¶
Use this property to render the ID of this field. For example, if you are manually constructing a
<label>in your template (despite the fact thatlabel_tag()will do this for you):<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>
Возвращает
True, если виджетBoundField“ скрыт.
- BoundField.label¶
The
labelof the field. This is used inlabel_tag().
- BoundField.name¶
The name of this field in the form:
>>> f = ContactForm() >>> print(f['subject'].name) subject >>> print(f['message'].name) message
- BoundField.widget_type¶
- New in Django 3.1.
Returns the lowercased class name of the wrapped field’s widget, with any trailing
inputorwidgetremoved. This may be used when building forms where the layout is dependent upon the widget type. For example:{% for field in form %} {% if field.widget_type == 'checkbox' %} # render one way {% else %} # render another way {% endif %} {% endfor %}
Методы BoundField¶
Возвращает 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)¶
When you use Django’s rendering shortcuts, CSS classes are used to indicate required form fields or fields that contain errors. If you’re manually rendering a form, you can access these CSS classes using the
css_classesmethod:>>> f = ContactForm(data={'message': ''}) >>> f['message'].css_classes() 'required'
If you want to provide some additional classes in addition to the error and required classes that may be required, you can provide those classes as an argument:
>>> f = ContactForm(data={'message': ''}) >>> f['message'].css_classes('foo bar') 'foo bar required'
- BoundField.label_tag(contents=None, attrs=None, label_suffix=None)¶
To separately render the label tag of a form field, you can call its
label_tag()method:>>> f = ContactForm(data={'message': ''}) >>> print(f['message'].label_tag()) <label for="id_message">Message:</label>
You can provide the
contentsparameter which will replace the auto-generated label tag. Anattrsdictionary may contain additional attributes for the<label>tag.The HTML that’s generated includes the form’s
label_suffix(a colon, by default) or, if set, the current field’slabel_suffix. The optionallabel_suffixparameter allows you to override any previously set suffix. For example, you can use an empty string to hide the label on selected fields. If you need to do this in a template, you could write a custom filter to allow passing parameters tolabel_tag.
- BoundField.value()¶
Use this method to render the raw value of this field as it would be rendered by a
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 немного отличается от работы с обычными формами.
Firstly, in order to upload files, you’ll need to make sure that your
<form> element correctly defines the enctype as
"multipart/form-data":
<form enctype="multipart/form-data" method="post" action="/foo/">
Secondly, when you use the form, you need to bind the file data. File
data is handled separately to normal form data, so when your form
contains a FileField and ImageField, you will need to specify
a second argument when you bind your form. So if we extend our
ContactForm to include an ImageField called mugshot, we
need to bind the file data containing the mugshot image:
# 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)
In practice, you will usually specify request.FILES as the source
of file data (just like you use request.POST as the source of
form data):
# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)
Constructing an unbound form is the same as always – omit both form data and file data:
# Unbound form with an image field
>>> f = ContactFormWithMugshot()
Определение таких форм¶
- Form.is_multipart()¶
If you’re writing reusable views or templates, you may not know ahead of time
whether your form is a multipart form or not. The is_multipart() method
tells you whether the form requires multipart encoding for submission:
>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True
Here’s an example of how you might use this in a template:
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>
Наследование форм¶
Если у вас есть несколько классов Form с одинаковыми полями, вы можете воспользоваться наследованием форм, чтобы убрать дублирование кода.
При наследовании определённого класса Form, результирующий класс будет обладать всеми полями родительского класса (-ов), включая поля, которые определены в нём самом.
In this example, ContactFormWithPriority contains all the fields from
ContactForm, plus an additional field, priority. The ContactForm
fields are ordered first:
>>> 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>
It’s possible to subclass multiple forms, treating forms as mixins. In this
example, BeatleForm subclasses both PersonForm and InstrumentForm
(in that order), and its field list includes the fields from the parent
classes:
>>> 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>
It’s possible to declaratively remove a Field inherited from a parent class
by setting the name of the field to None on the subclass. For example:
>>> 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¶
You can put several Django forms inside one <form> tag. To give each
Form its own namespace, use the prefix keyword argument:
>>> 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>
The prefix can also be specified on the form class:
>>> class PersonForm(forms.Form):
... ...
... prefix = 'person'