API форм¶
Об этом документе
В этом документе рассматриваются детали API форм Django. Сначала вам следует прочитать introduction to working with forms.
Связанные и несвязанные формы¶
Экземпляр Form
либо связан с набором данных, либо не связан.
- Если он связан с набором данных, он способен проверить эти данные и вывести форму в виде HTML с отображением данных в HTML.
- Если это unbound, он не может выполнить валидацию (потому что нет данных для валидации!), но он все еще может отобразить пустую форму как HTML.
-
class
Form
[исходный код]¶
Для создания несвязанного экземпляра Form
инстанцируйте класс:
>>> f = ContactForm()
Чтобы привязать данные к форме, передайте их в виде словаря в качестве первого параметра в конструктор вашего класса Form
:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
В этом словаре ключами являются имена полей, которые соответствуют атрибутам в вашем классе Form
. Значения - это данные, которые вы пытаетесь проверить. Обычно это строки, но нет требования, чтобы они были строками; тип передаваемых данных зависит от Field
, как мы увидим через некоторое время.
-
Form.
is_bound
¶
Если во время выполнения программы необходимо различать связанные и несвязанные экземпляры форм, проверьте значение атрибута формы is_bound
:
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({"subject": "hello"})
>>> f.is_bound
True
Обратите внимание, что передача пустого словаря создает связанную форму с пустыми данными:
>>> f = ContactForm({})
>>> f.is_bound
True
Если у вас есть связанный экземпляр Form
и вы хотите как-то изменить данные, или если вы хотите связать несвязанный экземпляр Form
с какими-то данными, создайте еще один экземпляр Form
. Не существует способа изменить данные в экземпляре Form
. После создания экземпляра Form
следует считать его данные неизменяемыми, независимо от того, есть у него данные или нет.
Использование форм для проверки данных¶
-
Form.
clean
()¶
Реализуйте метод clean()
на вашем Form
, когда вам необходимо добавить пользовательскую валидацию для полей, которые являются взаимозависимыми. Пример использования см. в Очистка и проверка полей, которые зависят друг от друга.
-
Form.
is_valid
()¶
Основной задачей объекта Form
является валидация данных. При наличии связанного экземпляра Form
вызовите метод is_valid()
для выполнения проверки и возврата булевой величины, указывающей, были ли данные валидными:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
Попробуем с некорректными данными. В данном случае subject
пуст (это ошибка, так как все поля по умолчанию обязательны для заполнения), а sender
не является действительным адресом электронной почты:
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
-
Form.
errors
¶
Обратитесь к атрибуту errors
для получения словаря сообщений об ошибках:
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
В этом словаре ключами являются имена полей, а значениями - списки строк, представляющих сообщения об ошибках. Сообщения об ошибках хранятся в виде списков, поскольку одно поле может иметь несколько сообщений об ошибках.
Вы можете получить доступ к errors
без необходимости сначала вызывать is_valid()
. Данные формы будут проверены при первом вызове is_valid()
или обращении к errors
.
Процедура валидации будет вызвана только один раз, независимо от того, сколько раз вы обращаетесь к errors
или вызываете is_valid()
. Это означает, что если валидация имеет побочные эффекты, то эти побочные эффекты будут вызваны только один раз.
-
Form.errors.
as_data
()¶
Возвращает dict
, который отображает поля на их исходные ValidationError
экземпляры.
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
Используйте этот метод в любое время, когда вам нужно идентифицировать ошибку по ее code
. Это позволяет, например, переписать сообщение об ошибке или написать пользовательскую логику в представлении при наличии данной ошибки. Его также можно использовать для сериализации ошибок в пользовательском формате (например, XML); например, as_json()
зависит от as_data()
.
Необходимость метода as_data()
обусловлена обратной совместимостью. Ранее экземпляры ValidationError
терялись, как только их рендеренные сообщения об ошибках добавлялись в словарь Form.errors
. В идеале Form.errors
должен был хранить экземпляры ValidationError
, а методы с префиксом as_
могли бы их рендерить, но пришлось сделать наоборот, чтобы не ломать код, ожидающий рендеринга сообщений об ошибках в Form.errors
.
-
Form.errors.
as_json
(escape_html=False)¶
Возвращает ошибки, сериализованные в виде JSON.
>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}
По умолчанию as_json()
не экранирует свой вывод. Если вы используете его для чего-то вроде AJAX-запросов к форме представления, где клиент интерпретирует ответ и вставляет ошибки на страницу, вы захотите убедиться, что результаты на стороне клиента экранированы, чтобы избежать возможности межсайтовой скриптинг-атаки. Вы можете сделать это в JavaScript с помощью element.textContent = errorText
или с помощью функции jQuery $(el).text(errorText)
(а не ее функции .html()
).
Если по какой-то причине вы не хотите использовать экранирование на стороне клиента, вы также можете установить escape_html=True
, и сообщения об ошибках будут экранированы, чтобы вы могли использовать их непосредственно в HTML.
-
Form.errors.
get_json_data
(escape_html=False)¶
Возвращает ошибки в виде словаря, пригодного для сериализации в JSON. Form.errors.as_json()
возвращает сериализованный JSON, в то время как это возвращает данные об ошибках до их сериализации.
Параметр escape_html
ведет себя так, как описано в Form.errors.as_json()
.
-
Form.
add_error
(field, error)¶
Этот метод позволяет добавлять ошибки в определенные поля из метода Form.clean()
или вообще извне формы, например, из представления.
Аргумент field
является именем поля, к которому должны быть добавлены ошибки. Если его значение равно None
, то ошибка будет рассматриваться как неполевая ошибка, возвращаемая Form.non_field_errors()
.
Аргумент error
может быть строкой или, предпочтительно, экземпляром ValidationError
. См. раздел Поднятие ValidationError о лучших практиках при определении ошибок формы.
Обратите внимание, что Form.add_error()
автоматически удаляет соответствующее поле из cleaned_data
.
-
Form.
has_error
(field, code=None)¶
Этот метод возвращает булево число, обозначающее, есть ли в поле ошибка с конкретной ошибкой code
. Если code
- None
, то он вернет True
, если поле вообще содержит какие-либо ошибки.
Для проверки ошибок, не связанных с полем, используйте NON_FIELD_ERRORS
в качестве параметра field
.
-
Form.
non_field_errors
()¶
Этот метод возвращает список ошибок из Form.errors
, которые не связаны с конкретным полем. Сюда входят ValidationError
ошибки, возникающие в Form.clean()
и ошибки, добавленные с помощью Form.add_error(None, "...")
.
Поведение несвязанных форм¶
Валидировать форму, в которой нет данных, бессмысленно, но, для справки, вот что происходит с несвязанными формами:
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Начальные значения формы¶
-
Form.
initial
¶
Используйте initial
для объявления начального значения полей формы во время выполнения. Например, вы можете захотеть заполнить поле username
именем пользователя текущей сессии.
Для этого необходимо использовать аргумент initial
для Form
. Этот аргумент, если он задан, должен представлять собой словарь, отображающий имена полей на начальные значения. В словарь следует включать только те поля, для которых задается начальное значение, не обязательно включать все поля формы. Например:
>>> f = ContactForm(initial={"subject": "Hi there!"})
Эти значения отображаются только для несвязанных форм, и они не используются в качестве резервных значений, если конкретное значение не предоставлено.
Если Field
определяет initial
и вы включаете initial
при инстанцировании Form
, то приоритет будет иметь последний initial
. В данном примере initial
предоставляется как на уровне поля, так и на уровне экземпляра формы, и приоритет имеет последний:
>>> from django import forms
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial="class")
... url = forms.URLField()
... comment = forms.CharField()
...
>>> f = CommentForm(initial={"name": "instance"}, auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" value="instance" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
-
Form.
get_initial_for_field
(field, field_name)¶
Возвращает исходные данные для поля формы. Он извлекает данные из Form.initial
, если они присутствуют, в противном случае пробует Field.initial
. Вызываемые значения оцениваются.
Рекомендуется использовать BoundField.initial
, а не get_initial_for_field()
, поскольку BoundField.initial
имеет более простой интерфейс. Кроме того, в отличие от get_initial_for_field()
, BoundField.initial
кэширует свои значения. Это особенно полезно при работе с callables, возвращаемые значения которых могут меняться (например, datetime.now
или uuid.uuid4
):
>>> import uuid
>>> class UUIDCommentForm(CommentForm):
... identifier = forms.UUIDField(initial=uuid.uuid4)
...
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields["identifier"], "identifier")
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f["identifier"].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
Проверка того, какие данные формы были изменены¶
-
Form.
has_changed
()¶
Используйте метод has_changed()
на вашем Form
, когда вам нужно проверить, были ли данные формы изменены по сравнению с начальными данными.
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
Когда форма отправлена, мы реконструируем ее и предоставляем исходные данные, чтобы можно было провести сравнение:
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
has_changed()
будет True
, если данные из request.POST
отличаются от тех, что были предоставлены в initial
или False
в противном случае. Результат вычисляется путем вызова Field.has_changed()
для каждого поля формы.
-
Form.
changed_data
¶
Атрибут changed_data
возвращает список имен полей, значения которых в связанных данных формы (обычно request.POST
) отличаются от того, что было предоставлено в initial
. Он возвращает пустой список, если данные не отличаются.
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
...
>>> f.changed_data
['subject', 'message']
Доступ к полям из формы¶
-
Form.
fields
¶
Доступ к полям экземпляра Form
можно получить из его атрибута fields
:
>>> for row in f.fields.values():
... print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields["name"]
<django.forms.fields.CharField object at 0x7ffaac6324d0>
Вы можете изменить поле и BoundField
экземпляра Form
, чтобы изменить способ его представления в форме:
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
>>> f["subject"].label = "Topic"
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Topic:</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
Остерегайтесь изменять атрибут base_fields
, поскольку эта модификация повлияет на все последующие экземпляры ContactForm
в рамках одного и того же процесса Python:
>>> f.base_fields["subject"].label_suffix = "?"
>>> another_f = CommentForm(auto_id=False)
>>> f.as_div().split("</div>")[0]
'<div><label for="id_subject">Subject?</label><input type="text" name="subject" maxlength="100" required id="id_subject">'
Доступ к «чистым» данным¶
-
Form.
cleaned_data
¶
Каждое поле в классе Form
отвечает не только за проверку данных, но и за их «очистку» - нормализацию до согласованного формата. Это хорошая особенность, поскольку она позволяет вводить данные для конкретного поля различными способами, всегда приводя к согласованному результату.
Например, DateField
нормализует входные данные в объект Python datetime.date
. Независимо от того, передаете ли вы ему строку в формате '1994-07-15'
, объект datetime.date
или ряд других форматов, DateField
всегда нормализует ее в объект datetime.date
, пока он действителен.
После создания экземпляра Form
с набором данных и его валидации можно получить доступ к чистым данным через его атрибут cleaned_data
:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
Обратите внимание, что любое текстовое поле - такое как CharField
или EmailField
- всегда очищает вводимые данные в строку. Мы рассмотрим последствия кодирования позже в этом документе.
Если данные не проходят валидацию, то словарь cleaned_data
содержит только валидные поля:
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> f.is_valid()
False
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there'}
cleaned_data
всегда будет только содержать ключ для полей, определенных в Form
, даже если вы передадите дополнительные данные при определении Form
. В данном примере мы передаем в конструктор ContactForm
кучу дополнительных полей, но cleaned_data
содержит только поля формы:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... "extra_field_1": "foo",
... "extra_field_2": "bar",
... "extra_field_3": "baz",
... }
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}
Если Form
действителен, cleaned_data
будет содержать ключ и значение для всех его полей, даже если данные не содержали значения для некоторых необязательных полей. В данном примере словарь данных не содержит значения для поля nick_name
, но cleaned_data
включает его, причем с пустым значением:
>>> from django import forms
>>> class OptionalPersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
... nick_name = forms.CharField(required=False)
...
>>> data = {"first_name": "John", "last_name": "Lennon"}
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': '', 'first_name': 'John', 'last_name': 'Lennon'}
В этом примере значение cleaned_data
для nick_name
установлено в пустую строку, потому что nick_name
- это CharField
, а CharField
s рассматривает пустые значения как пустую строку. Каждый тип поля знает, что такое «пустое» значение - например, для DateField
это None
вместо пустой строки. Для получения подробной информации о поведении каждого поля в этом случае, смотрите примечание «Пустое значение» для каждого поля в разделе «Встроенные классы Field
» ниже.
Вы можете написать код для выполнения валидации для отдельных полей формы (на основе их названия) или для формы в целом (учитывая комбинации различных полей). Более подробная информация об этом содержится в Валидация форм и полей.
Вывод форм в формате HTML¶
Вторая задача объекта Form
- отобразить себя в виде HTML. Для этого необходимо print
:
>>> f = ContactForm()
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself"></div>
Если форма привязана к данным, то HTML-вывод будет включать эти данные соответствующим образом. Например, если поле представлено символом <input type="text">
, то данные будут находиться в атрибуте value
. Если поле представлено символом <input type="checkbox">
, то в HTML-вывод будет включен атрибут checked
, если это необходимо:
>>> data = {
... "subject": "hello",
... "message": "Hi there",
... "sender": "foo@example.com",
... "cc_myself": True,
... }
>>> f = ContactForm(data)
>>> print(f)
<div><label for="id_subject">Subject:</label><input type="text" name="subject" value="hello" maxlength="100" required id="id_subject"></div>
<div><label for="id_message">Message:</label><input type="text" name="message" value="Hi there" required id="id_message"></div>
<div><label for="id_sender">Sender:</label><input type="email" name="sender" value="foo@example.com" required id="id_sender"></div>
<div><label for="id_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></div>
В этом выводе по умолчанию каждое поле обернуто символом <div>
. Обратите внимание на следующее:
- Для гибкости вывод не включает теги
<form>
и</form>
, а также тег<input type="submit">
. Это ваша работа. - Каждый тип поля имеет HTML-представление по умолчанию.
CharField
представляется символом<input type="text">
, аEmailField
-<input type="email">
.BooleanField(null=False)
представляется символом<input type="checkbox">
. Обратите внимание, что это всего лишь разумные значения по умолчанию; вы можете указать, какой HTML использовать для данного поля, используя виджеты, о которых мы расскажем в ближайшее время. - HTML
name
для каждого тега берется непосредственно из имени его атрибута в классеContactForm
. - Текстовая метка для каждого поля - например,
'Subject:'
,'Message:'
и'Cc myself:'
- генерируется из имени поля путем преобразования всех знаков подчеркивания в пробелы и перевода первой буквы в верхний регистр. Еще раз отметим, что это всего лишь разумные значения по умолчанию; вы также можете задать метки вручную. - Каждая текстовая метка окружена тегом HTML
<label>
, который указывает на соответствующее поле формы через егоid
. Егоid
, в свою очередь, формируется путем добавления'id_'
к имени поля. Атрибутыid
и теги<label>
включаются в вывод по умолчанию, чтобы следовать лучшим практикам, но вы можете изменить это поведение. - При выводе используется синтаксис HTML5, ориентированный на
<!DOCTYPE html>
. Например, он использует булевы атрибуты, такие какchecked
, а не стиль XHTMLchecked='checked'
.
Хотя стиль вывода <div>
является стилем по умолчанию, когда вы print
выводите форму, вы можете настроить вывод с помощью собственного шаблона формы, который может быть установлен для всего сайта, для каждой формы или для каждого экземпляра. См. раздел Многоразовые шаблоны форм.
Рендеринг по умолчанию¶
Рендеринг по умолчанию при print
форме использует следующие методы и атрибуты.
template_name
¶
-
Form.
template_name
¶
Имя шаблона, отображаемого при преобразовании формы в строку, например, через print(form)
или в шаблоне через {{ form }}
.
По умолчанию свойство, возвращающее значение form_template_name
рендерера. Вы можете задать его как строковое имя шаблона, чтобы переопределить его для конкретного класса формы.
render()
¶
-
Form.
render
(template_name=None, context=None, renderer=None)¶
Метод render вызывается методом __str__
, а также методами Form.as_div()
, Form.as_table()
, Form.as_p()
и Form.as_ul()
. Все аргументы необязательны и используются по умолчанию:
template_name
:Form.template_name
context
: Значение, возвращаемоеForm.get_context()
renderer
: Значение, возвращаемоеForm.default_renderer
Передавая template_name
, вы можете настроить шаблон, используемый только для одного вызова.
get_context()
¶
-
Form.
get_context
()¶
Возвращает контекст шаблона для рендеринга формы.
Доступный контекст:
form
: Связанная форма.fields
: Все связанные поля, кроме скрытых.hidden_fields
: Все скрытые связанные поля.errors
: Все ошибки формы, не связанные с полями или скрытыми полями.
template_name_label
¶
-
Form.
template_name_label
¶
Шаблон, используемый для отображения <label>
поля, используемый при вызове BoundField.label_tag()
/legend_tag()
. Может быть изменен для каждой формы путем переопределения этого атрибута или, в более общем случае, путем переопределения шаблона по умолчанию, см. также Переопределение встроенных шаблонов форм.
Стили вывода¶
Рекомендуемый подход к изменению стиля вывода формы - это установка пользовательского шаблона формы либо для всего сайта, либо для каждой формы, либо для каждого экземпляра. Примеры см. в разделе Многоразовые шаблоны форм.
Следующие вспомогательные функции предоставляются для обратной совместимости и являются прокси для Form.render()
передачи определенного значения template_name
.
Примечание
Из всех шаблонов и стилей вывода, предоставляемых фреймворком, шаблон по умолчанию as_div()
рекомендуется использовать вместо версий as_p()
, as_table()
и as_ul()
, поскольку в нем реализованы <fieldset>
и <legend>
для группировки связанных входов, и пользователям программы чтения с экрана легче ориентироваться.
Каждый помощник сопрягает метод формы с атрибутом, задающим соответствующее имя шаблона.
as_div()
¶
-
Form.
template_name_div
¶
Шаблон, используемый as_div()
. По умолчанию: 'django/forms/div.html'
.
-
Form.
as_div
()¶
as_div()
отображает форму как серию элементов <div>
, каждый из которых <div>
содержит одно поле, например:
>>> f = ContactForm()
>>> f.as_div()
… дает HTML как:
<div>
<label for="id_subject">Subject:</label>
<input type="text" name="subject" maxlength="100" required id="id_subject">
</div>
<div>
<label for="id_message">Message:</label>
<input type="text" name="message" required id="id_message">
</div>
<div>
<label for="id_sender">Sender:</label>
<input type="email" name="sender" required id="id_sender">
</div>
<div>
<label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
</div>
as_p()
¶
-
Form.
template_name_p
¶
Шаблон, используемый as_p()
. По умолчанию: 'django/forms/p.html'
.
-
Form.
as_p
()¶
as_p()
отображает форму в виде серии тегов <p>
, каждый из которых <p>
содержит одно поле:
>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>\n<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>\n<p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" required></p>\n<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>
as_ul()
¶
-
Form.
template_name_ul
¶
Шаблон, используемый as_ul()
. По умолчанию: 'django/forms/ul.html'
.
-
Form.
as_ul
()¶
as_ul()
отображает форму в виде серии тегов <li>
, каждый из которых <li>
содержит одно поле. Он не включает <ul>
или </ul>
, так что для гибкости можно указать любые HTML-атрибуты в <ul>
:
>>> f = ContactForm()
>>> f.as_ul()
'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required></li>
<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></li>
as_table()
¶
-
Form.
template_name_table
¶
Шаблон, используемый as_table()
. По умолчанию: 'django/forms/table.html'
.
-
Form.
as_table
()¶
as_table()
отображает форму в виде HTML <table>
:
>>> f = ContactForm()
>>> f.as_table()
'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>'
>>> print(f)
<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)
<div class="required"><label for="id_subject" class="required">Subject:</label> ...
<div class="required"><label for="id_message" class="required">Message:</label> ...
<div class="required"><label for="id_sender" class="required">Sender:</label> ...
<div><label for="id_cc_myself">Cc myself:</label> ...
>>> f["subject"].label_tag()
<label class="required" for="id_subject">Subject:</label>
>>> f["subject"].legend_tag()
<legend class="required" for="id_subject">Subject:</legend>
>>> f["subject"].label_tag(attrs={"class": "foo"})
<label for="id_subject" class="foo required">Subject:</label>
>>> f["subject"].legend_tag(attrs={"class": "foo"})
<legend for="id_subject" class="foo required">Subject:</legend>
Настройка атрибутов 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)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
Если для auto_id
установлено значение True
, то вывод формы будет включать теги <label>
и использовать имя поля в качестве id
для каждого поля формы:
>>> f = ContactForm(auto_id=True)
>>> print(f)
<div><label for="subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="subject"></div>
<div><label for="message">Message:</label><textarea name="message" cols="40" rows="10" required id="message"></textarea></div>
<div><label for="sender">Sender:</label><input type="email" name="sender" required id="sender"></div>
<div><label for="cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="cc_myself"></div>
Если в качестве auto_id
задана строка, содержащая символ формата '%s'
, то вывод формы будет включать теги <label>
и генерировать атрибуты id
на основе строки формата. Например, для строки формата 'field_%s'
поле с именем subject
получит значение id
'field_subject'
. Продолжаем наш пример:
>>> f = ContactForm(auto_id="id_for_%s")
>>> print(f)
<div><label for="id_for_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message:</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender:</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself:</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
Если auto_id
установлено в любое другое истинное значение - например, в строку, которая не включает %s
- то библиотека будет действовать так, как будто auto_id
- это True
.
По умолчанию auto_id
устанавливается в строку 'id_%s'
.
-
Form.
label_suffix
¶
Переводимая строка (по умолчанию двоеточие (:
) на английском языке), которая будет добавлена после любого имени метки при отображении формы.
С помощью параметра label_suffix
можно настроить этот символ или вовсе опустить его:
>>> f = ContactForm(auto_id="id_for_%s", label_suffix="")
>>> print(f)
<div><label for="id_for_subject">Subject</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message</label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender</label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself</label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
>>> f = ContactForm(auto_id="id_for_%s", label_suffix=" ->")
>>> print(f)
<div><label for="id_for_subject">Subject:</label><input type="text" name="subject" maxlength="100" required id="id_for_subject"></div>
<div><label for="id_for_message">Message -></label><textarea name="message" cols="40" rows="10" required id="id_for_message"></textarea></div>
<div><label for="id_for_sender">Sender -></label><input type="email" name="sender" required id="id_for_sender"></div>
<div><label for="id_for_cc_myself">Cc myself -></label><input type="checkbox" name="cc_myself" id="id_for_cc_myself"></div>
Обратите внимание, что суффикс метки добавляется только в том случае, если последний символ метки не является знаком препинания (в английском языке это .
, !
, ?
или :
).
Поля также могут определять свои собственные label_suffix
. Это будет иметь приоритет перед Form.label_suffix
. Суффикс также может быть переопределен во время выполнения с помощью параметра label_suffix
к label_tag()
/ legend_tag()
.
-
Form.
use_required_attribute
¶
Если установлено значение True
(по умолчанию), обязательные поля формы будут иметь HTML-атрибут required
.
Formsets инстанцируйте формы с помощью use_required_attribute=False
, чтобы избежать неправильной валидации браузера при добавлении и удалении форм из набора форм.
Настройка отображения виджетов формы¶
-
Form.
default_renderer
¶
Определяет renderer, который будет использоваться для формы. По умолчанию установлено значение None
, что означает использование рендеринга по умолчанию, заданного параметром FORM_RENDERER
.
Вы можете задать это как атрибут класса при объявлении формы или использовать аргумент renderer
для Form.__init__()
. Например:
from django import forms
class MyForm(forms.Form):
default_renderer = MyRenderer()
или:
form = MyForm(renderer=MyRenderer())
Примечания по заказу полевых работ¶
В ярлыках as_p()
, as_ul()
и as_table()
поля отображаются в том порядке, в котором вы определили их в классе формы. Например, в примере ContactForm
поля определены в порядке subject
, message
, sender
, cc_myself
. Чтобы изменить порядок вывода HTML, измените порядок, в котором эти поля перечислены в классе.
Существует еще несколько способов индивидуализации заказа:
-
Form.
field_order
¶
По умолчанию Form.field_order=None
, что сохраняет порядок, в котором вы определили поля в классе формы. Если field_order
является списком имен полей, то поля упорядочиваются так, как указано в списке, а оставшиеся поля добавляются в соответствии с порядком по умолчанию. Неизвестные имена полей в списке игнорируются. Это позволяет отключить поле в подклассе, установив его в None
без необходимости переопределять порядок.
Вы также можете использовать аргумент Form.field_order
в Form
для переопределения порядка полей. Если Form
определяет field_order
и вы включаете field_order
при инстанцировании Form
, то последний field_order
будет иметь приоритет.
-
Form.
order_fields
(field_order)¶
Вы можете переставлять поля в любое время, используя order_fields()
со списком имен полей, как в field_order
.
Как отображаются ошибки¶
Если вы выводите связанный объект Form
, то при выводе автоматически выполняется проверка формы, если она еще не была выполнена, и в HTML-вывод будут включены ошибки проверки в виде <ul class="errorlist">
рядом с полем. Конкретное расположение сообщений об ошибках зависит от используемого метода вывода:
>>> data = {
... "subject": "",
... "message": "Hi there",
... "sender": "invalid email address",
... "cc_myself": True,
... }
>>> f = ContactForm(data, auto_id=False)
>>> print(f)
<div>Subject:
<ul class="errorlist"><li>This field is required.</li></ul>
<input type="text" name="subject" maxlength="100" required aria-invalid="true">
</div>
<div>Message:
<textarea name="message" cols="40" rows="10" required>Hi there</textarea>
</div>
<div>Sender:
<ul class="errorlist"><li>Enter a valid email address.</li></ul>
<input type="email" name="sender" value="invalid email address" required aria-invalid="true">
</div>
<div>Cc myself:
<input type="checkbox" name="cc_myself" checked>
</div>
Настройка формата списка ошибок¶
-
class
ErrorList
(initlist=None, error_class=None, renderer=None)¶ По умолчанию формы используют
django.forms.utils.ErrorList
для форматирования ошибок валидации.ErrorList
представляет собой объект типа списка, гдеinitlist
- это список ошибок. Кроме того, этот класс имеет следующие атрибуты и методы.-
error_class
¶ Классы CSS, которые будут использоваться при отображении списка ошибок. Любые предоставленные классы добавляются к классу по умолчанию
errorlist
.
-
renderer
¶ Определяет renderer, который будет использоваться для
ErrorList
. По умолчанию используетсяNone
, что означает использование рендерера по умолчанию, заданного параметромFORM_RENDERER
.
-
template_name
¶ Имя шаблона, используемого при вызове
__str__
илиrender()
. По умолчанию это'django/forms/errors/list/default.html'
, который является прокси для шаблона'ul.html'
.
-
template_name_text
¶ Имя шаблона, используемого при вызове
as_text()
. По умолчанию это'django/forms/errors/list/text.html'
. Этот шаблон отображает ошибки в виде списка пунктов.
-
template_name_ul
¶ Имя шаблона, используемого при вызове
as_ul()
. По умолчанию это'django/forms/errors/list/ul.html'
. Этот шаблон отображает ошибки в тегах<li>
с оберткой<ul>
с классами CSS, определеннымиerror_class
.
-
get_context
()¶ Возвращает контекст для отображения ошибок в шаблоне.
Доступный контекст:
errors
: Список ошибок.error_class
: Строка классов CSS.
-
render
(template_name=None, context=None, renderer=None)¶ Метод render вызывается как методом
__str__
, так и методомas_ul()
.Все аргументы являются необязательными и будут использоваться по умолчанию:
template_name
: Значение, возвращаемоеtemplate_name
context
: Значение, возвращаемоеget_context()
renderer
: Значение, возвращаемоеrenderer
-
as_text
()¶ Выводит список ошибок, используя шаблон, определенный
template_name_text
.
-
as_ul
()¶ Выводит список ошибок, используя шаблон, определенный
template_name_ul
.
Если вы хотите настроить отображение ошибок, это можно сделать, переопределив атрибут
template_name
или, в более общем случае, переопределив шаблон по умолчанию, см. также Переопределение встроенных шаблонов форм.-
Более детальный вывод¶
Методы 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 aria-invalid="true"> >>> f["message"].errors ['This field is required.'] >>> print(f["message"].errors) <ul class="errorlist"><li>This field is required.</li></ul> >>> f["subject"].errors [] >>> print(f["subject"].errors) >>> str(f["subject"].errors) ''
При отображении поля с ошибками на виджете поля будет установлено значение
aria-invalid="true"
, чтобы указать пользователям программы чтения с экрана на наличие ошибки.Changed in Django 5.0:aria-invalid="true"
был добавлен, когда поле содержит ошибки.
-
BoundField.
field
¶ Экземпляр формы
Field
из класса формы, в который обернут этотBoundField
.
-
BoundField.
form
¶ Экземпляр
Form
, к которому привязан данныйBoundField
.
-
BoundField.
html_name
¶ Имя, которое будет использоваться в HTML атрибуте виджета
name
. При этом учитывается формаprefix
.
-
BoundField.
id_for_label
¶ Используйте это свойство для отображения ID этого поля. Например, если вы вручную создаете
<label>
в своем шаблоне (несмотря на то, чтоlabel_tag()
/legend_tag()
сделает это за вас):<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
По умолчанию это будет имя поля с префиксом
id_
(»id_my_field
» для примера выше). Вы можете изменить ID, установивattrs
в виджете поля. Например, объявив поле следующим образом:my_field = forms.CharField(widget=forms.TextInput(attrs={"id": "myFIELD"}))
и, используя вышеприведенный шаблон, можно получить что-то вроде:
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
-
BoundField.
initial
¶ Используйте
BoundField.initial
для получения исходных данных для поля формы. Он извлекает данные изForm.initial
, если они присутствуют, в противном случае пробуетField.initial
. Вызываемые значения оцениваются. Дополнительные примеры см. в разделе Начальные значения формы.BoundField.initial
кэширует свое возвращаемое значение, что особенно полезно при работе с вызываемыми переменными, возвращаемые значения которых могут изменяться (например,datetime.now
илиuuid.uuid4
):>>> from datetime import datetime >>> class DatedCommentForm(CommentForm): ... created = forms.DateTimeField(initial=datetime.now) ... >>> f = DatedCommentForm() >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54) >>> f["created"].initial datetime.datetime(2021, 7, 27, 9, 5, 54)
Рекомендуется использовать
BoundField.initial
вместоget_initial_for_field()
.
Возвращает
True
, если виджет этогоBoundField
скрыт.
-
BoundField.
label
¶ label
поля. Используется вlabel_tag()
/legend_tag()
.
-
BoundField.
name
¶ Имя этого поля в форме:
>>> f = ContactForm() >>> print(f["subject"].name) subject >>> print(f["message"].name) message
-
BoundField.
template_name
¶ - New in Django 5.0.
Имя шаблона, отображаемого с помощью
BoundField.as_field_group()
.Свойство, возвращающее значение
template_name
, если установлено, иначеfield_template_name
.
-
BoundField.
use_fieldset
¶ Возвращает значение атрибута
use_fieldset
этого виджета BoundField.
-
BoundField.
widget_type
¶ Возвращает имя класса виджета обернутого поля в нижнем регистре, при этом удаляются лишние символы
input
илиwidget
. Это может быть использовано при построении форм, в которых компоновка зависит от типа виджета. Например:{% for field in form %} {% if field.widget_type == 'checkbox' %} # render one way {% else %} # render another way {% endif %} {% endfor %}
Методы BoundField
¶
-
BoundField.
as_field_group
()¶ - New in Django 5.0.
Отображает поле, используя
BoundField.render()
со значениями по умолчанию, которые отображаютBoundField
, включая его метку, текст подсказки и ошибки, используяtemplate_name
шаблона, если установлено иначеfield_template_name
.
Возвращает строку HTML для представления этого как
<input type="hidden">
.**kwargs
передаются вas_widget()
.Этот метод используется в основном для внутренних целей. Вместо него следует использовать виджет.
-
BoundField.
as_widget
(widget=None, attrs=None, only_initial=False)[исходный код]¶ Рендерит поле путем рендеринга переданного виджета, добавляя любые HTML-атрибуты, переданные как
attrs
. Если виджет не указан, то будет использован виджет поля по умолчанию.only_initial
используется внутренними модулями Django и не должен быть установлен явно.
-
BoundField.
css_classes
(extra_classes=None)[исходный код]¶ Когда вы используете ярлыки рендеринга Django, CSS-классы используются для обозначения обязательных полей формы или полей, содержащих ошибки. При ручном рендеринге формы доступ к этим CSS-классам можно получить с помощью метода
css_classes
:>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes() 'required'
Если вы хотите предоставить какие-то дополнительные классы в дополнение к классам error и required, которые могут потребоваться, вы можете предоставить эти классы в качестве аргумента:
>>> f = ContactForm(data={"message": ""}) >>> f["message"].css_classes("foo bar") 'foo bar required'
-
BoundField.
get_context
()¶ - New in Django 5.0.
Возвращает контекст шаблона для рендеринга поля. Доступным контекстом является
field
, являющийся экземпляром связанного поля.
-
BoundField.
label_tag
(contents=None, attrs=None, label_suffix=None, tag=None)[исходный код]¶ Отображает тег label для поля формы, используя шаблон, указанный
Form.template_name_label
.Доступный контекст:
field
: Этот экземплярBoundField
.contents
: По умолчанию конкатенированная строка изBoundField.label
иForm.label_suffix
(илиField.label_suffix
, если задано). Это можно переопределить аргументамиcontents
иlabel_suffix
.attrs
:dict
, содержащийfor
,Form.required_css_class
иid
.id
генерируется виджетом поляattrs
илиBoundField.auto_id
. Дополнительные атрибуты могут быть предоставлены аргументомattrs
.use_tag
: Булево значение, которое равноTrue
, если метка имеетid
. ЕслиFalse
, шаблон по умолчанию опускаетtag
.tag
: Необязательная строка для настройки тега, по умолчаниюlabel
.
Совет
В вашем шаблоне
field
является экземпляромBoundField
. Поэтомуfield.field
обращается кBoundField.field
, будучи полем, которое вы объявили, например,forms.CharField
.Чтобы отдельно отобразить тег label поля формы, можно вызвать его метод
label_tag()
:>>> f = ContactForm(data={"message": ""}) >>> print(f["message"].label_tag()) <label for="id_message">Message:</label>
Если вы хотите настроить рендеринг, это можно сделать, переопределив атрибут
Form.template_name_label
или, в более общем случае, переопределив шаблон по умолчанию, см. также Переопределение встроенных шаблонов форм.
-
BoundField.
legend_tag
(contents=None, attrs=None, label_suffix=None)[исходный код]¶ Вызывает
label_tag()
сtag='legend'
для рендеринга метки с тегами<legend>
. Это полезно при рендеринге виджетов радио и нескольких флажков, где<legend>
может быть более уместным, чем<label>
.
-
BoundField.
render
(template_name=None, context=None, renderer=None)¶ - New in Django 5.0.
Метод рендеринга вызывается
as_field_group
. Все аргументы необязательны и используются по умолчанию:template_name
:BoundField.template_name
context
: Значение, возвращаемоеBoundField.get_context()
renderer
: Значение, возвращаемоеForm.default_renderer
Передавая
template_name
, вы можете настроить шаблон, используемый только для одного вызова.
-
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", b"file data")}
>>> f = ContactFormWithMugshot(data, file_data)
На практике в качестве источника данных файла обычно указывается request.FILES
(точно так же, как в качестве источника данных формы используется request.POST
):
# Bound form with an image field, data from the request
>>> f = ContactFormWithMugshot(request.POST, request.FILES)
Конструирование несвязанной формы происходит так же, как и всегда - опускаются как данные формы, так и данные файла:
# Unbound form with an image field
>>> f = ContactFormWithMugshot()
Тестирование многочастных форм¶
-
Form.
is_multipart
()¶
Если вы пишете многократно используемые представления или шаблоны, вы можете не знать заранее, является ли ваша форма многокомпонентной или нет. Метод is_multipart()
позволяет определить, требует ли форма многочастной кодировки для отправки:
>>> f = ContactFormWithMugshot()
>>> f.is_multipart()
True
Вот пример того, как это можно использовать в шаблоне:
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% endif %}
{{ form }}
</form>
Подклассификация форм¶
Если у вас есть несколько классов Form
, которые имеют общие поля, вы можете использовать подклассификацию для устранения избыточности.
Когда вы создаете подкласс пользовательского класса Form
, полученный подкласс будет включать все поля родительского класса (классов), а затем поля, которые вы определите в подклассе.
В данном примере ContactFormWithPriority
содержит все поля из ContactForm
, плюс дополнительное поле priority
. Поля ContactForm
упорядочены в первую очередь:
>>> class ContactFormWithPriority(ContactForm):
... priority = forms.CharField()
...
>>> f = ContactFormWithPriority(auto_id=False)
>>> print(f)
<div>Subject:<input type="text" name="subject" maxlength="100" required></div>
<div>Message:<textarea name="message" cols="40" rows="10" required></textarea></div>
<div>Sender:<input type="email" name="sender" required></div>
<div>Cc myself:<input type="checkbox" name="cc_myself"></div>
<div>Priority:<input type="text" name="priority" required></div>
Можно подклассифицировать несколько форм, рассматривая формы как миксины. В данном примере BeatleForm
является подклассом PersonForm
и InstrumentForm
(именно в таком порядке), и его список полей включает поля из родительских классов:
>>> from django import forms
>>> class PersonForm(forms.Form):
... first_name = forms.CharField()
... last_name = forms.CharField()
...
>>> class InstrumentForm(forms.Form):
... instrument = forms.CharField()
...
>>> class BeatleForm(InstrumentForm, PersonForm):
... haircut_type = forms.CharField()
...
>>> b = BeatleForm(auto_id=False)
>>> print(b)
<div>First name:<input type="text" name="first_name" required></div>
<div>Last name:<input type="text" name="last_name" required></div>
<div>Instrument:<input type="text" name="instrument" required></div>
<div>Haircut type:<input type="text" name="haircut_type" required></div>
Можно декларативно удалить 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
¶
В один тег <form>
можно поместить несколько Django-форм. Чтобы присвоить каждой Form
собственное пространство имен, используйте аргумент prefix
:
>>> mother = PersonForm(prefix="mother")
>>> father = PersonForm(prefix="father")
>>> print(mother)
<div><label for="id_mother-first_name">First name:</label><input type="text" name="mother-first_name" required id="id_mother-first_name"></div>
<div><label for="id_mother-last_name">Last name:</label><input type="text" name="mother-last_name" required id="id_mother-last_name"></div>
>>> print(father)
<div><label for="id_father-first_name">First name:</label><input type="text" name="father-first_name" required id="id_father-first_name"></div>
<div><label for="id_father-last_name">Last name:</label><input type="text" name="father-last_name" required id="id_father-last_name"></div>
Префикс также может быть указан на классе формы:
>>> class PersonForm(forms.Form):
... ...
... prefix = "person"
...