API форм¶
Об этом документе
В этом документе рассматриваются детали API форм Django. Сначала вам следует прочитать introduction to working with forms.
Связанные и несвязанные формы¶
Экземпляр Form либо связан с набором данных, либо не связан.
- Если он связан с набором данных, он способен проверить эти данные и вывести форму в виде HTML с отображением данных в HTML.
- Если это unbound, он не может выполнить валидацию (потому что нет данных для валидации!), но он все еще может отобразить пустую форму как HTML.
-
class
Form[исходный код]¶
Чтобы создать несвязанный экземпляр Form, инстанцируйте класс:
>>> f = ContactForm()
Чтобы привязать данные к форме, передайте их в виде словаря в качестве первого параметра в конструктор вашего класса Form:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
В этом словаре ключами являются имена полей, которые соответствуют атрибутам в вашем классе Form. Значения - это данные, которые вы пытаетесь проверить. Обычно это строки, но нет требования, чтобы они были строками; тип передаваемых данных зависит от Field, как мы увидим через некоторое время.
-
Form.is_bound¶
Если вам необходимо различать связанные и несвязанные экземпляры формы во время выполнения, проверьте значение атрибута формы is_bound:
>>> f = ContactForm()
>>> f.is_bound
False
>>> f = ContactForm({'subject': 'hello'})
>>> f.is_bound
True
Обратите внимание, что передача пустого словаря создает связанную форму с пустыми данными:
>>> f = ContactForm({})
>>> f.is_bound
True
Если у вас есть связанный экземпляр Form и вы хотите как-то изменить данные, или если вы хотите связать несвязанный экземпляр Form с какими-то данными, создайте еще один экземпляр Form. Не существует способа изменить данные в экземпляре Form. После создания экземпляра Form следует считать его данные неизменяемыми, независимо от того, есть у него данные или нет.
Использование форм для проверки данных¶
-
Form.clean()¶
Реализуйте метод clean() на вашем Form, когда вам необходимо добавить пользовательскую валидацию для полей, которые являются взаимозависимыми. Пример использования см. в Очистка и проверка полей, которые зависят друг от друга.
-
Form.is_valid()¶
Основная задача объекта Form заключается в проверке данных. При наличии связанного экземпляра Form вызовите метод is_valid(), чтобы запустить проверку и вернуть булево значение, указывающее, были ли данные достоверными:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
Давайте попробуем с недействительными данными. В данном случае subject пуст (ошибка, так как по умолчанию все поля обязательны для заполнения), а sender не является действительным адресом электронной почты:
>>> data = {'subject': '',
... 'message': 'Hi there',
... 'sender': 'invalid email address',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
-
Form.errors¶
Обратитесь к атрибуту errors, чтобы получить словарь сообщений об ошибках:
>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
В этом словаре ключами являются имена полей, а значениями - списки строк, представляющих сообщения об ошибках. Сообщения об ошибках хранятся в виде списков, поскольку одно поле может иметь несколько сообщений об ошибках.
Вы можете получить доступ к errors без необходимости сначала вызывать is_valid(). Данные формы будут проверены при первом вызове is_valid() или обращении к errors.
Процедура валидации будет вызвана только один раз, независимо от того, сколько раз вы обращаетесь к errors или вызываете is_valid(). Это означает, что если валидация имеет побочные эффекты, то эти побочные эффекты будут вызваны только один раз.
-
Form.errors.as_data()¶
Возвращает dict, который отображает поля на их исходные ValidationError экземпляры.
>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
Используйте этот метод в любое время, когда вам нужно идентифицировать ошибку по ее code. Это позволяет, например, переписать сообщение об ошибке или написать пользовательскую логику в представлении при наличии данной ошибки. Его также можно использовать для сериализации ошибок в пользовательском формате (например, XML); например, as_json() зависит от as_data().
Необходимость метода as_data() обусловлена обратной совместимостью. Ранее экземпляры ValidationError терялись, как только их рендеренные сообщения об ошибках добавлялись в словарь Form.errors. В идеале Form.errors должен был хранить экземпляры ValidationError, а методы с префиксом as_ могли бы их рендерить, но пришлось сделать наоборот, чтобы не ломать код, ожидающий рендеринга сообщений об ошибках в Form.errors.
-
Form.errors.as_json(escape_html=False)¶
Возвращает ошибки, сериализованные в виде JSON.
>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}
По умолчанию as_json() не экранирует свой вывод. Если вы используете его для чего-то вроде AJAX-запросов к форме представления, где клиент интерпретирует ответ и вставляет ошибки на страницу, вы захотите убедиться, что результаты на стороне клиента экранированы, чтобы избежать возможности межсайтовой скриптинг-атаки. Вы можете сделать это в JavaScript с помощью element.textContent = errorText или с помощью функции jQuery $(el).text(errorText) (а не ее функции .html()).
Если по какой-то причине вы не хотите использовать экранирование на стороне клиента, вы также можете установить escape_html=True, и сообщения об ошибках будут экранированы, чтобы вы могли использовать их непосредственно в HTML.
-
Form.errors.get_json_data(escape_html=False)¶
Возвращает ошибки в виде словаря, пригодного для сериализации в JSON. Form.errors.as_json() возвращает сериализованный JSON, в то время как это возвращает данные об ошибках до их сериализации.
Параметр escape_html ведет себя так, как описано в Form.errors.as_json().
-
Form.add_error(field, error)¶
Этот метод позволяет добавлять ошибки в определенные поля из метода Form.clean() или вообще извне формы, например, из представления.
Аргумент field является именем поля, к которому должны быть добавлены ошибки. Если его значение равно None, то ошибка будет рассматриваться как неполевая ошибка, возвращаемая Form.non_field_errors().
Аргумент error может быть строкой или, предпочтительно, экземпляром ValidationError. См. раздел Поднятие ValidationError о лучших практиках при определении ошибок формы.
Обратите внимание, что Form.add_error() автоматически удаляет соответствующее поле из cleaned_data.
-
Form.has_error(field, code=None)¶
Этот метод возвращает булево число, обозначающее, есть ли в поле ошибка с конкретной ошибкой code. Если code - None, то он вернет True, если поле вообще содержит какие-либо ошибки.
Для проверки ошибок, не связанных с полем, используйте NON_FIELD_ERRORS в качестве параметра field.
-
Form.non_field_errors()¶
Этот метод возвращает список ошибок из Form.errors, которые не связаны с конкретным полем. Сюда входят ValidationErrorошибки, возникающие в Form.clean() и ошибки, добавленные с помощью Form.add_error(None, "...").
Поведение несвязанных форм¶
Бессмысленно проверять форму без данных, но, для справки, вот что происходит с несвязанными формами:
>>> f = ContactForm()
>>> f.is_valid()
False
>>> f.errors
{}
Начальные значения формы¶
-
Form.initial¶
Используйте initial для объявления начального значения полей формы во время выполнения. Например, вы можете захотеть заполнить поле username именем пользователя текущей сессии.
Чтобы добиться этого, используйте аргумент initial для Form. Этот аргумент, если он задан, должен представлять собой словарь, отображающий имена полей на начальные значения. Включите только те поля, для которых вы указываете начальное значение; нет необходимости включать каждое поле в вашу форму. Например:
>>> f = ContactForm(initial={'subject': 'Hi there!'})
Эти значения отображаются только для несвязанных форм, и они не используются в качестве резервных значений, если конкретное значение не предоставлено.
Если Field определяет initial и вы включаете initial при инстанцировании Form, то последний initial будет иметь приоритет. В данном примере initial предоставляется как на уровне поля, так и на уровне экземпляра формы, и последний имеет приоритет:
>>> from django import forms
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='class')
... url = forms.URLField()
... comment = forms.CharField()
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required></td></tr>
-
Form.get_initial_for_field(field, field_name)¶
Возвращает исходные данные для поля формы. Он извлекает данные из Form.initial, если они присутствуют, в противном случае пробует Field.initial. Вызываемые значения оцениваются.
Рекомендуется использовать BoundField.initial вместо get_initial_for_field(), поскольку BoundField.initial имеет более простой интерфейс. Кроме того, в отличие от get_initial_for_field(), BoundField.initial кэширует свои значения. Это особенно полезно при работе с callables, возвращаемые значения которых могут меняться (например, datetime.now или uuid.uuid4):
>>> import uuid
>>> class UUIDCommentForm(CommentForm):
... identifier = forms.UUIDField(initial=uuid.uuid4)
>>> f = UUIDCommentForm()
>>> f.get_initial_for_field(f.fields['identifier'], 'identifier')
UUID('972ca9e4-7bfe-4f5b-af7d-07b3aa306334')
>>> f.get_initial_for_field(f.fields['identifier'], 'identifier')
UUID('1b411fab-844e-4dec-bd4f-e9b0495f04d0')
>>> # Using BoundField.initial, for comparison
>>> f['identifier'].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
>>> f['identifier'].initial
UUID('28a09c59-5f00-4ed9-9179-a3b074fa9c30')
Проверка того, какие данные формы были изменены¶
-
Form.has_changed()¶
Используйте метод has_changed() на вашем Form, когда вам нужно проверить, были ли данные формы изменены по сравнению с начальными данными.
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data, initial=data)
>>> f.has_changed()
False
Когда форма отправлена, мы реконструируем ее и предоставляем исходные данные, чтобы можно было провести сравнение:
>>> f = ContactForm(request.POST, initial=data)
>>> f.has_changed()
has_changed() будет True, если данные из request.POST отличаются от тех, что были предоставлены в initial или False в противном случае. Результат вычисляется путем вызова Field.has_changed() для каждого поля формы.
-
Form.changed_data¶
Атрибут changed_data возвращает список имен полей, значения которых в связанных данных формы (обычно request.POST) отличаются от того, что было предоставлено в initial. Он возвращает пустой список, если данные не отличаются.
>>> f = ContactForm(request.POST, initial=data)
>>> if f.has_changed():
... print("The following fields changed: %s" % ", ".join(f.changed_data))
>>> f.changed_data
['subject', 'message']
Доступ к полям из формы¶
-
Form.fields¶
Вы можете получить доступ к полям экземпляра Form из его атрибута fields:
>>> for row in f.fields.values(): print(row)
...
<django.forms.fields.CharField object at 0x7ffaac632510>
<django.forms.fields.URLField object at 0x7ffaac632f90>
<django.forms.fields.CharField object at 0x7ffaac3aa050>
>>> f.fields['name']
<django.forms.fields.CharField object at 0x7ffaac6324d0>
Вы можете изменить поле экземпляра 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, а CharFields рассматривает пустые значения как пустую строку. Каждый тип поля знает, что такое «пустое» значение - например, для DateField это None вместо пустой строки. Для получения подробной информации о поведении каждого поля в этом случае, смотрите примечание «Пустое значение» для каждого поля в разделе «Встроенные классы Field» ниже.
Вы можете написать код для выполнения валидации для отдельных полей формы (на основе их названия) или для формы в целом (учитывая комбинации различных полей). Более подробная информация об этом содержится в Валидация форм и полей.
Вывод форм в формате HTML¶
Вторая задача объекта Form - отобразить себя в виде HTML. Чтобы сделать это, print его:
>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself"></td></tr>
Если форма связана с данными, то HTML-вывод будет включать эти данные соответствующим образом. Например, если поле представлено символом <input type="text">, то данные будут находиться в атрибуте value. Если поле представлено символом <input type="checkbox">, то HTML-вывод будет включать checked, если это уместно:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo@example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" required></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" required></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" required></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked></td></tr>
Этот вывод по умолчанию представляет собой двухколоночную HTML-таблицу, с <tr> для каждого поля. Обратите внимание на следующее:
- Для гибкости вывод не включает теги
<table>и</table>, а также теги<form>и</form>или тег<input type="submit">. Это ваша работа, чтобы сделать это. - Каждый тип поля имеет HTML-представление по умолчанию.
CharFieldпредставляется символом<input type="text">, аEmailField-<input type="email">.BooleanField(null=False)представляется символом<input type="checkbox">. Обратите внимание, что это всего лишь разумные значения по умолчанию; вы можете указать, какой HTML использовать для данного поля, используя виджеты, о которых мы расскажем в ближайшее время. - HTML
nameдля каждого тега берется непосредственно из имени его атрибута в классеContactForm. - Текстовая метка для каждого поля - например,
'Subject:','Message:'и'Cc myself:'- генерируется из имени поля путем преобразования всех знаков подчеркивания в пробелы и перевода первой буквы в верхний регистр. Еще раз отметим, что это всего лишь разумные значения по умолчанию; вы также можете задать метки вручную. - Каждая текстовая метка окружена тегом HTML
<label>, который указывает на соответствующее поле формы через егоid. Егоid, в свою очередь, формируется путем добавления'id_'к имени поля. Атрибутыidи теги<label>включаются в вывод по умолчанию, чтобы следовать лучшим практикам, но вы можете изменить это поведение. - При выводе используется синтаксис HTML5, ориентированный на
<!DOCTYPE html>. Например, он использует булевы атрибуты, такие какchecked, а не стиль XHTMLchecked='checked'.
Хотя вывод <table> является стилем вывода по умолчанию, когда вы print форму, доступны и другие стили вывода. Каждый стиль доступен как метод на объекте формы, и каждый метод вывода возвращает строку.
template_name¶
-
Form.template_name¶
Имя шаблона, который будет отображаться, если форма приведена к строке, например, через print(form) или в шаблоне через {{ form }}. По умолчанию этим шаблоном является 'django/forms/default.html', который является прокси для 'django/forms/table.html'. Шаблон может быть изменен для каждой формы путем переопределения атрибута template_name или в более общем случае путем переопределения шаблона по умолчанию, см. также Переопределение встроенных шаблонов форм.
template_name_label¶
-
Form.template_name_label¶
Шаблон, используемый для отображения <label> поля, используемый при вызове BoundField.label_tag(). Может быть изменен для каждой формы путем переопределения этого атрибута или, в более общем случае, путем переопределения шаблона по умолчанию, см. также Переопределение встроенных шаблонов форм.
as_p()¶
-
Form.as_p()¶
as_p() отображает форму, используя шаблон, назначенный атрибуту формы template_name_p, по умолчанию это шаблон 'django/forms/p.html'. Этот шаблон отображает форму в виде серии тегов <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() отображает форму, используя шаблон, назначенный атрибуту формы template_name_ul, по умолчанию это шаблон 'django/forms/ul.html'. Этот шаблон отображает форму в виде серии тегов <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() выводит форму с помощью шаблона, назначенного атрибуту формы template_name_table, по умолчанию это шаблон 'django/forms/table.html'. Этот шаблон выводит форму в виде 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>
get_context()¶
-
Form.get_context()¶
Возвращает контекст для рендеринга формы в шаблоне.
Доступный контекст:
form: Связанная форма.fields: Все связанные поля, кроме скрытых.hidden_fields: Все скрытые связанные поля.errors: Все ошибки формы, не связанные с полями или скрытыми полями.
render()¶
-
Form.render(template_name=None, context=None, renderer=None)¶
Метод render вызывается методом __str__, а также методами Form.as_table(), Form.as_p() и Form.as_ul(). Все аргументы являются необязательными и используются по умолчанию:
template_name:Form.template_namecontext: Значение, возвращаемоеForm.get_context()renderer: Значение, возвращаемоеForm.default_renderer
Стилизация требуемых или ошибочных строк формы¶
-
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>
Настройка формата списка ошибок¶
-
class
ErrorList(initlist=None, error_class=None, renderer=None)¶ По умолчанию формы используют
django.forms.utils.ErrorListдля форматирования ошибок валидации.ErrorListпредставляет собой объект типа списка, гдеinitlist- это список ошибок. Кроме того, этот класс имеет следующие атрибуты и методы.-
error_class¶ Классы CSS, которые будут использоваться при отображении списка ошибок. Любые предоставленные классы добавляются к классу по умолчанию
errorlist.
-
renderer¶ - New in Django 4.0.
Определяет renderer, который будет использоваться для
ErrorList. По умолчанию используетсяNone, что означает использование рендерера по умолчанию, заданного параметромFORM_RENDERER.
-
template_name¶ - New in Django 4.0.
Имя шаблона, используемого при вызове
__str__илиrender(). По умолчанию это'django/forms/errors/list/default.html', который является прокси для шаблона'ul.html'.
-
template_name_text¶ - New in Django 4.0.
Имя шаблона, используемого при вызове
as_text(). По умолчанию это'django/forms/errors/list/text.html'. Этот шаблон отображает ошибки в виде списка пунктов.
-
template_name_ul¶ - New in Django 4.0.
Имя шаблона, используемого при вызове
as_ul(). По умолчанию это'django/forms/errors/list/ul.html'. Этот шаблон отображает ошибки в тегах<li>с оберткой<ul>с классами CSS, определеннымиerror_class.
-
get_context()¶ - New in Django 4.0.
Возвращает контекст для отображения ошибок в шаблоне.
Доступный контекст:
errors: Список ошибок.error_class: Строка классов CSS.
-
render(template_name=None, context=None, renderer=None)¶ - New in Django 4.0.
Метод render вызывается как методом
__str__, так и методомas_ul().Все аргументы являются необязательными и будут использоваться по умолчанию:
template_name: Значение, возвращаемоеtemplate_namecontext: Значение, возвращаемоеget_context()renderer: Значение, возвращаемоеrenderer
-
as_text()¶ Выводит список ошибок, используя шаблон, определенный
template_name_text.
-
as_ul()¶ Выводит список ошибок, используя шаблон, определенный
template_name_ul.
Если вы хотите настроить отображение ошибок, это можно сделать, переопределив атрибут
template_nameили, в более общем случае, переопределив шаблон по умолчанию, см. также Переопределение встроенных шаблонов форм.-
Рендеринг ErrorList был перенесен в шаблонизатор.
Не рекомендуется, начиная с версии 4.0: Возможность возвращать str при вызове метода __str__ устарела. Вместо этого используйте шаблонизатор, который возвращает SafeString.
Более детальный вывод¶
Методы as_p(), as_ul() и as_table() являются ярлыками - это не единственный способ отображения объекта формы.
-
class
BoundField[исходный код]¶ Используется для отображения HTML или атрибутов доступа для одного поля экземпляра
Form.Метод
__str__()этого объекта отображает HTML для данного поля.
Чтобы получить одно BoundField, используйте синтаксис поиска по словарю в вашей форме, используя имя поля в качестве ключа:
>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" required>
Чтобы получить все объекты BoundField, выполните итерацию формы:
>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" required>
<input type="text" name="message" id="id_message" required>
<input type="email" name="sender" id="id_sender" required>
<input type="checkbox" name="cc_myself" id="id_cc_myself">
Вывод для конкретного поля соответствует настройке auto_id объекта формы:
>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" required>
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" required>
Атрибуты BoundField¶
-
BoundField.auto_id¶ Атрибут HTML ID для данного
BoundField. Возвращает пустую строку, еслиForm.auto_idявляетсяFalse.
-
BoundField.data¶ Это свойство возвращает данные для этого
BoundField, извлеченные методомvalue_from_datadict()виджета, илиNone, если они не были переданы:>>> unbound_form = ContactForm() >>> print(unbound_form['subject'].data) None >>> bound_form = ContactForm(data={'subject': 'My Subject'}) >>> print(bound_form['subject'].data) My Subject
-
BoundField.errors¶ list-like object, который при печати отображается как HTML
<ul class="errorlist">:>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} >>> f = ContactForm(data, auto_id=False) >>> print(f['message']) <input type="text" name="message" required> >>> f['message'].errors ['This field is required.'] >>> print(f['message'].errors) <ul class="errorlist"><li>This field is required.</li></ul> >>> f['subject'].errors [] >>> print(f['subject'].errors) >>> str(f['subject'].errors) ''
-
BoundField.field¶ Экземпляр формы
Fieldиз класса формы, в который обернут этотBoundField.
-
BoundField.form¶ Экземпляр
Form, к которому привязан данныйBoundField.
-
BoundField.html_name¶ Имя, которое будет использоваться в HTML атрибуте виджета
name. При этом учитывается формаprefix.
-
BoundField.id_for_label¶ Используйте это свойство для отображения ID этого поля. Например, если вы вручную создаете
<label>в своем шаблоне (несмотря на то, чтоlabel_tag()сделает это за вас):<label for="{{ form.my_field.id_for_label }}">...</label>{{ my_field }}
По умолчанию это будет имя поля с префиксом
id_(»id_my_field» для примера выше). Вы можете изменить ID, установивattrsв виджете поля. Например, объявив поле следующим образом:my_field = forms.CharField(widget=forms.TextInput(attrs={'id': 'myFIELD'}))
и, используя вышеприведенный шаблон, можно получить что-то вроде:
<label for="myFIELD">...</label><input id="myFIELD" type="text" name="my_field" required>
-
BoundField.initial¶ Используйте
BoundField.initialдля получения исходных данных для поля формы. Он извлекает данные изForm.initial, если они присутствуют, в противном случае пробуетField.initial. Вызываемые значения оцениваются. Дополнительные примеры см. в разделе Начальные значения формы.BoundField.initialкэширует свое возвращаемое значение, что особенно полезно при работе с callables, возвращаемые значения которых могут изменяться (например,datetime.nowилиuuid.uuid4):>>> from datetime import datetime >>> class DatedCommentForm(CommentForm): ... created = forms.DateTimeField(initial=datetime.now) >>> f = DatedCommentForm() >>> f['created'].initial datetime.datetime(2021, 7, 27, 9, 5, 54) >>> f['created'].initial datetime.datetime(2021, 7, 27, 9, 5, 54)
Рекомендуется использовать
BoundField.initialвместоget_initial_for_field().
Возвращает
True, если виджет этогоBoundFieldскрыт.
-
BoundField.label¶ label> поля. Это используется вlabel_tag().
-
BoundField.name¶ Имя этого поля в форме:
>>> f = ContactForm() >>> print(f['subject'].name) subject >>> print(f['message'].name) message
-
BoundField.widget_type¶ Возвращает имя класса виджета обернутого поля в нижнем регистре, с удаленными концевыми строками
inputилиwidget. Это может быть использовано при создании форм, в которых компоновка зависит от типа виджета. Например:{% for field in form %} {% if field.widget_type == 'checkbox' %} # render one way {% else %} # render another way {% endif %} {% endfor %}
Методы BoundField¶
Возвращает строку HTML для представления этого как
<input type="hidden">.**kwargsпередаются вas_widget().Этот метод используется в основном для внутренних целей. Вместо него следует использовать виджет.
-
BoundField.as_widget(widget=None, attrs=None, only_initial=False)[исходный код]¶ Рендерит поле путем рендеринга переданного виджета, добавляя любые HTML-атрибуты, переданные как
attrs. Если виджет не указан, то будет использован виджет поля по умолчанию.only_initialиспользуется внутренними модулями Django и не должен быть установлен явно.
-
BoundField.css_classes(extra_classes=None)[исходный код]¶ Когда вы используете ярлыки рендеринга 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 для поля формы, используя шаблон, указанный
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, шаблон по умолчанию опускает тег<label>.
Совет
В вашем шаблоне
fieldявляется экземпляромBoundField. Поэтомуfield.fieldобращается кBoundField.field, будучи полем, которое вы объявили, например,forms.CharField.Чтобы отдельно отобразить тег label поля формы, можно вызвать его метод
label_tag():>>> f = ContactForm(data={'message': ''}) >>> print(f['message'].label_tag()) <label for="id_message">Message:</label>
Если вы хотите настроить рендеринг, это можно сделать, переопределив атрибут
Form.template_name_labelили, в более общем случае, переопределив шаблон по умолчанию, см. также Переопределение встроенных шаблонов форм.Changed in Django 4.0:Теперь этикетка отображается с помощью шаблонизатора.
-
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'