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, такой как 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
s рассматривает пустые значения как пустую строку. Каждый тип поля знает, что такое «пустое» значение - например, для 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
, а не стиль XHTMLchecked='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
. На самом деле, когда вы 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
.
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_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
[исходный код]¶ Используется для отображения 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.
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>
Возвращает
True
, если виджет этогоBoundField
скрыт.
-
BoundField.
label
¶ label
> поля. Это используется вlabel_tag()
.
-
BoundField.
name
¶ Имя этого поля в форме:
>>> f = ContactForm() >>> print(f['subject'].name) subject >>> print(f['message'].name) message
Методы 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
()[исходный код]¶ Когда вы используете ярлыки рендеринга 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)[исходный код]¶ Чтобы отдельно отобразить тег label поля формы, можно вызвать его метод
label_tag()
:>>> f = ContactForm(data={'message': ''}) >>> print(f['message'].label_tag()) <label for="id_message">Message:</label>
Вы можете предоставить параметр
contents
, который заменит автоматически сгенерированный тег label. Словарьattrs
может содержать дополнительные атрибуты для тега<label>
.Генерируемый HTML содержит суффикс
label_suffix
формы (двоеточие, по умолчанию) или, если задан, суффиксlabel_suffix
текущего поля. Необязательный параметрlabel_suffix
позволяет отменить любой ранее установленный суффикс. Например, вы можете использовать пустую строку, чтобы скрыть метку для выбранных полей. Если вам нужно сделать это в шаблоне, вы можете написать пользовательский фильтр, позволяющий передавать параметры вlabel_tag
.
-
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_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:
>>> class PersonForm(forms.Form):
... ...
... prefix = 'person'