Работа с формами
О документе
Этот документ предоставляет введение в основы веб-форм и их обработку в Django. Для более подробного ознакомления со специфическими областями API форм, см. API форм, Поля форм и Валидация форм и полей.
Если вы не планируете создавать веб-сайты и приложения, которые только публикуют контент и не принимают входные данные от посетителей, вам необходимо понять и использовать формы.
Django предоставляет ряд инструментов и библиотек, которые помогут вам создавать формы для приема входных данных от посетителей сайта, а затем обрабатывать и реагировать на эти данные.
HTML-формы
В HTML форма представляет собой набор элементов внутри <form>...</form>, которые позволяют посетителю выполнять такие действия, как ввод текста, выбор параметров, манипулирование объектами или элементами управления и т. д., а затем отправлять эту информацию обратно на сервер.
Некоторые из этих элементов интерфейса формы — текстовые поля или флажки — довольно просты и встроенны в сам HTML. Другие намного сложнее; интерфейс, который открывает календарь выбора даты или позволяет перемещать ползунок или управлять элементами управления, обычно использует JavaScript и CSS, а также элементы HTML-формы <input> для достижения этих эффектов.
Помимо своих <input> элементов, форма должна указать два момента:
- куда: URL, на который должны быть возвращены данные, соответствующие вводу пользователя
- как: метод HTTP, которым должны быть возвращены данные
Например, форма входа в Django admin содержит несколько <input> элементов: одно type="text" для имени пользователя, одно type="password" для пароля и одно type="submit" для кнопки «Войти». Она также содержит некоторые скрытые текстовые поля, которые пользователь не видит, которые Django использует для определения дальнейших действий.
Она также сообщает браузеру, что данные формы должны быть отправлены на URL, указанный в <form>’s action атрибуте — /admin/ — и что они должны быть отправлены с помощью HTTP-механизма, указанного в method атрибуте — post.
Когда элемент <input type="submit" value="Log in"> срабатывает, данные возвращаются на /admin/.
GET и POST
GET и POST — единственные методы HTTP, которые следует использовать при работе с формами.
Форма входа Django возвращается с помощью метода POST, в котором браузер собирает данные формы, кодирует их для передачи, отправляет на сервер, а затем получает ответ.
GET, в отличие от этого, упаковывает отправленные данные в строку и использует ее для составления URL. URL содержит адрес, куда должны быть отправлены данные, а также ключи и значения данных. Вы можете увидеть это в действии, если выполните поиск в документации Django, что приведет к URL-адресу вида https://docs.djangoproject.com/search/?q=forms&release=1.
GET и POST обычно используются для разных целей.
Любой запрос, который может быть использован для изменения состояния системы — например, запрос, производящий изменения в базе данных — должен использовать POST. GET следует использовать только для запросов, не влияющих на состояние системы.
GET также был бы неприемлем для формы пароля, потому что пароль отобразится в URL и, следовательно, также в истории браузера и журналах сервера, в открытом виде. Он также не подходит для большого количества данных или для бинарных данных, таких как изображение. Веб-приложение, использующее GET запросы для форм администрирования, представляет собой риск для безопасности: злоумышленнику может быть легко подделать запрос формы для получения доступа к чувствительным частям системы. POST, в сочетании с другими мерами защиты, такими как защита от CSRF в Django, обеспечивает больший контроль над доступом.
С другой стороны, GET подходит для таких вещей, как форма веб-поиска, потому что URL-адреса, представляющие запрос GET, легко закладки, могут быть распространены или повторно отправлены.
Роль Django в формах
Обработка форм — сложная задача. Рассмотрим Django admin, где множество элементов данных различных типов может потребоваться подготовить для отображения в форме, преобразовать в HTML, редактировать с помощью удобного интерфейса, вернуть на сервер, валидировать и очистить, а затем сохранить или передать для дальнейшей обработки.
Функциональность форм Django может упростить и автоматизировать значительную часть этой работы, а также выполнить ее более безопасно, чем большинство программистов могут сделать в написанном ими коде.
Django обрабатывает три отдельных части работы, связанной с формами:
- подготовка и реструктурирование данных для подготовки к отображению
- создание HTML-форм для данных
- прием и обработка отправленных форм и данных от клиента
Возможна написания кода, который выполняет все это вручную, но Django может позаботиться обо всем за вас.
Формы в Django
Мы кратко описали HTML-формы, но HTML-форма <form> — это всего лишь одна часть необходимого механизма.
В контексте веб-приложения «форма» может относиться к этой HTML-форме <form>, к Django Form, которая ее генерирует, к структурированным данным, возвращаемым при отправке, или к завершенному рабочему набору этих частей.
Класс Django Form
В основе этой системы компонентов лежит класс Django Form. Так же, как модель Django описывает логическую структуру объекта, его поведение и способ представления его частей, класс Form описывает форму и определяет ее работу и внешний вид.
Так же, как поля класса модели соответствуют полям базы данных, поля класса формы соответствуют элементам HTML-формы <input>. (Класс ModelForm сопоставляет поля класса модели с элементами HTML-формы <input> через Form; именно на этом основана форма Django admin.)
Поля формы — это классы; они управляют данными формы и выполняют валидацию при отправке формы. DateField и FileField обрабатывают очень разные типы данных и выполняют с ними разные действия.
Поле формы представляется пользователю в браузере как HTML-«виджет» — часть механизма пользовательского интерфейса. Каждый тип поля имеет соответствующий по умолчанию класс Виджет, но их можно переопределить по необходимости.
Создание, обработка и отображение форм
При отображении объекта в Django, как правило:
- получаем его в представлении (например, извлекаем из базы данных)
- передаем его в контекст шаблона
- преобразуем его в HTML-разметку с использованием переменных шаблона
Отображение формы в шаблоне включает почти ту же работу, что и отображение любого другого типа объекта, но есть некоторые ключевые различия.
В случае экземпляра модели, содержащей нет данных, редко бывает полезно что-либо делать с ним в шаблоне. С другой стороны, имеет смысл отобразить пустую форму — это то, что мы делаем, когда хотим, чтобы пользователь ее заполнил.
Таким образом, когда мы обрабатываем экземпляр модели в представлении, мы обычно извлекаем его из базы данных. Когда мы работаем с формой, мы обычно создаем ее экземпляр в представлении.
Когда мы создаем экземпляр формы, мы можем выбрать оставить ее пустой или предварительно заполнить ее, например, с помощью:
- данных из сохраненного экземпляра модели (как в случае форм администрирования для редактирования)
- данных, собранных из других источников
- данных, полученных при предыдущей отправке HTML-формы
Последний из этих случаев наиболее интересен, потому что именно он позволяет пользователям не только просматривать веб-сайт, но и отправлять информацию на него.
Создание формы
Работа, которую нужно выполнить
Предположим, вы хотите создать простую форму на своем веб-сайте для получения имени пользователя. Вам потребуется что-то вроде этого в вашем шаблоне:
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
Это сообщает браузеру вернуть данные формы по адресу /your-name/, используя метод POST. Он отобразит текстовое поле с меткой «Ваше имя:» и кнопку «ОК». Если в контексте шаблона есть переменная current_name, она будет использована для предварительного заполнения поля your_name.
Вам понадобится представление, которое отображает шаблон, содержащий HTML-форму, и которое может предоставить поле current_name соответствующим образом.
При отправке формы запрос POST, отправленный на сервер, будет содержать данные формы.
Теперь вам также понадобится представление, соответствующее этому URL-адресу /your-name/, которое найдет соответствующие пары ключ/значение в запросе и обработает их.
Это очень простая форма. На практике форма может содержать десятки или сотни полей, многие из которых могут потребоваться предварительно заполнить, и мы можем ожидать, что пользователь несколько раз пройдёт цикл редактирования-отправки, прежде чем завершит операцию.
Возможно, потребуется выполнить некоторую валидацию в браузере, даже прежде чем форма будет отправлена; мы можем захотеть использовать гораздо более сложные поля, которые позволяют пользователю, например, выбирать даты из календаря и т. д.
В этот момент гораздо проще заставить Django выполнить большую часть этой работы.
Создание формы в Django
Класс Form
Мы уже знаем, как должен выглядеть наш HTML-форма. Наш отправной пункт в Django выглядит так:
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
Это определяет класс Form с одним полем (your_name). Мы применили удобочитаемое имя поля, которое будет отображаться в <label> при рендеринге (хотя в этом случае, указанное нами label фактически такое же, что было бы сгенерировано автоматически, если бы мы его не указали).
Максимальная допустимая длина поля определяется параметром max_length. Это выполняет два действия. Оно устанавливает maxlength="100" на HTML-<input> (так что браузер должен предотвратить ввод пользователем большего количества символов). Это также означает, что когда Django получит форму из браузера, он проверит длину данных.
Экземпляр класса Form имеет метод is_valid(), который выполняет процедуры валидации для всех его полей. Когда этот метод вызывается, если все поля содержат допустимые данные, он:
- вернёт
True - поместит данные формы в атрибут
cleaned_data.
Вся форма при первом отображении будет выглядеть следующим образом:
<label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" maxlength="100" required />
Обратите внимание, что она не содержит теги <form> или кнопку отправки. Нам придется предоставить их самим в шаблоне.
Обработчик
Данные формы, отправленные на веб-сайт Django, обрабатываются обработчиком, как правило, тем же обработчиком, который опубликовал форму. Это позволяет нам повторно использовать часть логики.
Для обработки формы нам нужно создать её экземпляр в обработчике для URL, по которому мы хотим её опубликовать:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
Если мы попадаем в этот обработчик с запросом GET, он создаст пустой экземпляр формы и поместит его в контекст шаблона для отображения. Вот что должно произойти при первом посещении URL.
Если форма отправляется с помощью запроса POST, обработчик снова создаст экземпляр формы и заполнит его данными из запроса: form =
NameForm(request.POST) Это называется «связыванием данных с формой» (теперь это связанная форма).
Мы вызываем метод is_valid() формы; если он не True, мы возвращаемся в шаблон с формой. На этот раз форма уже не пустая (несвязанная), поэтому HTML-форма будет заполнена ранее отправленными данными, где их можно будет отредактировать и исправить по необходимости.
Если is_valid() является True, мы теперь сможем найти все валидированные данные формы в атрибуте cleaned_data. Мы можем использовать эти данные для обновления базы данных или выполнения других операций перед отправкой HTTP-перенаправления браузеру, указывающего, куда нужно перейти дальше.
Шаблон
Нам не нужно делать много в нашем name.html шаблоне. Самый простой пример:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
Все поля формы и их атрибуты будут распакованы в HTML-разметку из этого {{ form }} языком шаблонов Django.
Формы и защита от межсайтовых поддельных запросов
Django поставляется с удобной в использовании защитой от межсайтовых поддельных запросов. При отправке формы через POST с включенной защитой CSRF необходимо использовать тег шаблона csrf_token, как в предыдущем примере. Однако, поскольку защита CSRF не связана напрямую с формами в шаблонах, этот тег опущен в последующих примерах в этом документе.
HTML5 типы ввода и валидация браузером
Если ваша форма содержит URLField, EmailField или любой целочисленный тип поля, Django будет использовать url, email и number HTML5 типы ввода. По умолчанию браузеры могут применять свою валидацию к этим полям, которая может быть строже, чем валидация Django. Если вы хотите отключить это поведение, установите атрибут novalidate на теге form или укажите другой виджет для поля, например TextInput.
Теперь у нас есть рабочая веб-форма, описанная Django Form, обработанная обработчиком и отображенная как HTML-<form>.
Этого достаточно для начала работы, но фреймворк форм предоставляет гораздо больше возможностей. После понимания основ описанного выше процесса вы должны быть готовы понять другие особенности системы форм и готовы узнать больше об underlying механизмах.
Подробнее о классах форм Django
Все классы форм создаются как подклассы django.forms.Form, включая ModelForm, с которым вы сталкиваетесь в админ-панели Django.
Модели и формы
На самом деле, если ваша форма будет использоваться для непосредственного добавления или редактирования Django-модели, ModelForm может сэкономить вам много времени, усилий и кода, поскольку он создаст форму вместе с соответствующими полями и их атрибутами из класса Model.
Связанные и несвязанные экземпляры форм
Различие между связанными и несвязанными формами важно:
- Несвязанная форма не имеет связанных с ней данных. При отображении пользователю она будет пустой или содержать значения по умолчанию.
- Связанная форма имеет отправленные данные и, следовательно, может быть использована для определения, являются ли эти данные валидными. Если рендерится невалидная связанная форма, она может содержать сообщения об ошибках, показывающие пользователю, какие данные нужно исправить.
Атрибут формы is_bound покажет, связаны ли с формой данные или нет.
Подробнее о полях
Рассмотрим более полезную форму, чем наш минимальный пример выше, которую мы можем использовать для реализации функциональности «свяжитесь со мной» на личном сайте:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
Наша предыдущая форма использовала одно поле, your_name, CharField. В этом случае наша форма имеет четыре поля: subject, message, sender и cc_myself. CharField, EmailField и BooleanField — всего лишь три доступных типа полей; полный список можно найти в Поля форм.
Виджеты
Каждое поле формы имеет соответствующий Класс виджета, который, в свою очередь, соответствует HTML-виджету формы, например, <input
type="text">.
В большинстве случаев у поля будет разумный виджет по умолчанию. Например, по умолчанию CharField будет иметь виджет TextInput, который генерирует <input type="text"> в HTML. Если вам нужен <textarea>, вы укажете соответствующий виджет при определении поля формы, как мы сделали для поля message.
Данные поля
Какие бы данные ни были отправлены с формой, после успешной валидации, вызвав is_valid() (и is_valid() вернул True), валидированные данные формы будут в словаре form.cleaned_data. Эти данные будут преобразованы в типы Python для вас.
Примечание
Вы по-прежнему можете получить доступ к невалидированным данным напрямую из request.POST на этом этапе, но валидированные данные предпочтительнее.
В примере формы связи выше, cc_myself будет булевым значением. Точно так же поля, такие как IntegerField и FloatField, преобразуют значения в целые числа Python и вещественные числа соответственно.
Вот как данные формы могут быть обработаны в обработчике, который обрабатывает эту форму:
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['info@example.com']
if cc_myself:
recipients.append(sender)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
Подсказка
Дополнительную информацию о отправке писем из Django см. в Отправка писем.
Некоторые типы полей требуют дополнительной обработки. Например, файлы, загруженные с помощью формы, необходимо обрабатывать по-другому (их можно получить из request.FILES, а не из request.POST). Подробности о том, как обрабатывать загрузки файлов с помощью вашей формы, см. в Связывание загруженных файлов с формой.
Работа с шаблонами форм
Для добавления вашей формы в шаблон необходимо поместить экземпляр формы в контекст шаблона. Таким образом, если ваша форма называется form в контексте, то {{ form }} отобразит ее элементы <label> и <input> соответствующим образом.
Параметры отображения форм
Дополнительные элементы шаблона формы
Не забудьте, что вывод формы не включает окружающие <form> теги или элемент управления submit формы. Вы должны предоставить их самостоятельно.
Существуют и другие варианты вывода для пар <label>/<input>:
-
{{ form.as_table }}отобразит их как ячейки таблицы, заключенные в теги<tr> -
{{ form.as_p }}отобразит их, заключенные в теги<p> -
{{ form.as_ul }}отобразит их, заключенные в теги<li>
Обратите внимание, что вам необходимо самостоятельно предоставить окружающие элементы <table> или <ul>.
Вот вывод {{ form.as_p }} для нашего экземпляра ContactForm:
<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>
<textarea name="message" id="id_message" required></textarea></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>
Обратите внимание, что каждое поле формы имеет атрибут ID, установленный в id_<field-name>, который ссылается на сопроводительный тег метки. Это важно для обеспечения доступности форм для вспомогательных технологий, таких как программы чтения с экрана. Вы также можете настроить способ генерации меток и идентификаторов.
См. Вывод форм в виде HTML для получения дополнительной информации об этом.
Ручное отображение полей
Мы можем вручную распаковать поля формы, если захотим (например, чтобы переупорядочить поля). Каждое поле доступно как атрибут формы с помощью {{ form.name_of_field }}, и в шаблоне Django будет отображено соответствующим образом. Например:
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
Полные элементы <label> также можно сгенерировать с помощью label_tag(). Например:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
Отображение сообщений об ошибках формы
Конечно, цена этой гибкости — большая работа. До сих пор мы не беспокоились о том, как отображать ошибки формы, так как это делается за нас. В этом примере нам пришлось убедиться, что мы обрабатываем любые ошибки для каждого поля и любые ошибки для формы в целом. Обратите внимание на {{ form.non_field_errors
}} вверху формы и поиск ошибок в каждом поле в шаблоне.
Использование {{ form.name_of_field.errors }} отображает список ошибок формы, отображаемых как список без маркировки. Это может выглядеть так:
<ul class="errorlist">
<li>Sender is required.</li>
</ul>
Список имеет класс CSS errorlist, чтобы вы могли стилизовать его внешний вид. Если вы хотите дополнительно настроить отображение ошибок, вы можете сделать это, проходя по ним:
{% if form.subject.errors %}
<ol>
{% for error in form.subject.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
Ошибки, не относящиеся к полям (и/или ошибки скрытых полей, которые отображаются в верхней части формы при использовании помощников, таких как form.as_p()), будут отображаться с дополнительным классом nonfield, чтобы помочь отличить их от ошибок, относящихся к конкретным полям. Например, {{ form.non_field_errors }} будет выглядеть так:
<ul class="errorlist nonfield">
<li>Generic validation error</li>
</ul>
См. API форм для получения дополнительной информации об ошибках, стилях и работе с атрибутами формы в шаблонах.
Проход по полям формы
Если вы используете один и тот же HTML для каждого поля формы, вы можете сократить дублирование кода, пройдя по каждому полю поочерёдно с помощью цикла {% for %}:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
Полезные атрибуты для {{ field }} включают:
-
{{ field.label }} - Метка поля, например
Email address. -
{{ field.label_tag }} -
Метка поля, заключенная в соответствующий HTML-тег
<label>. Это включает суффикс метки формыlabel_suffix. Например, по умолчаниюlabel_suffixявляется двоеточием:<label for="id_email">Email address:</label>
-
{{ field.id_for_label }} - Идентификатор, который будет использоваться для этого поля (
id_emailв примере выше). Если вы создаете метку вручную, вы можете использовать ее вместоlabel_tag. Она также полезна, например, если у вас есть какой-то встроенный JavaScript и вы хотите избежать жесткой кодировки идентификатора поля. -
{{ field.value }} - Значение поля. Например,
someone@example.com. -
{{ field.html_name }} - Имя поля, которое будет использоваться в поле имени элемента ввода. Это учитывает префикс формы, если он был задан.
-
{{ field.help_text }} - Любой текст справки, который был связан с полем.
-
{{ field.errors }} - Выводит
<ul class="errorlist">, содержащий любые ошибки проверки, соответствующие этому полю. Вы можете настроить отображение ошибок с помощью цикла{% for error in field.errors %}. В этом случае каждый объект в цикле — это простая строка, содержащая сообщение об ошибке. -
{{ field.is_hidden }} - Этот атрибут
True, если поле формы является скрытым полем, иFalseв противном случае. Он не особенно полезен как переменная шаблона, но может быть полезен в условных проверках, таких как:
{% if field.is_hidden %}
{# Do something special #}
{% endif %}
-
{{ field.field }} - Экземпляр
Fieldиз класса формы, который оборачивает этоBoundField. Вы можете использовать его для доступа к атрибутамField, например,{{ char_field.field.max_length }}.
См. также
Полный список атрибутов и методов см. в BoundField.
Проход по скрытым и видимым полям
Django предоставляет два метода для прохода по скрытым и видимым полям независимо: hidden_fields() и visible_fields(). Вот модификация предыдущего примера, использующая эти два метода:
{# Include the hidden fields #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{# Include the visible fields #}
{% for field in form.visible_fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
В этом примере не обрабатываются ошибки скрытых полей. Обычно ошибка скрытого поля указывает на подделку формы, так как обычное взаимодействие с формой не изменяет их. Однако вы можете легко добавить отображение ошибок для этих ошибок формы.
Многоразовые шаблоны форм
Если ваш сайт использует одинаковую логику отображения форм в нескольких местах, вы можете сократить дублирование, сохранив цикл формы в автономном шаблоне и используя тег include для его повторного использования в других шаблонах:
# In your form template:
{% include "form_snippet.html" %}
# In form_snippet.html:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
Если объект формы, передаваемый в шаблон, имеет другое имя в контексте, вы можете использовать алиас с помощью аргумента with тега include:
{% include "form_snippet.html" with form=comment_form %}
Если вы часто делаете это, вы можете рассмотреть создание пользовательского тега вставки.
Дополнительные темы
Это основы, но формы могут делать гораздо больше:
-
Наборы форм
- Использование начальных данных с набором форм
- Ограничение максимального количества форм
- Валидация набора форм
- Валидация количества форм в наборе форм
- Обработка упорядочения и удаления форм
- Добавление дополнительных полей в набор форм
- Передача пользовательских параметров формам набора форм
- Использование набора форм в представлениях и шаблонах
- Создание форм из моделей
-
Файлы форм (класс
Media)
См. также
- Справочник по формам
- Покрывает полную справочную информацию по API, включая поля форм, виджеты форм и валидацию форм и полей.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.11/topics/forms/index/