Работа с формами
О документе
В данном документе представлено введение в основы веб-форм и их обработку в 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> атрибуте 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, в сочетании с другими средствами защиты, такими как защита Django от CSRF, обеспечивает больший контроль над доступом.
С другой стороны, GET подходит для таких вещей, как форма поиска по веб-сайту, так как URL, которые представляют собой запрос GET, могут быть легко занесены в закладки, разделены или повторно отправлены.
Роль Django в формах
Обработка форм — непростая задача. Рассмотрим администраторскую панель Django, где множество данных различных типов могут потребоваться подготовить для отображения в форме, преобразовать в 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.)
Поля формы сами по себе являются классами; они управляют данными формы и выполняют валидацию при отправке формы. Класс 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>
Это указывает браузеру вернуть данные формы на URL /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 типы input и проверка браузером
Если ваша форма включает URLField, EmailField или любой целочисленный тип поля, Django будет использовать url, email и number типы HTML5 input. По умолчанию браузеры могут применять собственные правила проверки на эти поля, которые могут быть строже, чем проверки Django. Если вы хотите отключить это поведение, установите атрибут novalidate на теге form, или укажите другой виджет для поля, например TextInput.
Теперь у нас есть рабочая веб-форма, описанная Django Form, обработанная представлением и отображённая в HTML <form>.
Этого достаточно для начала, но фреймворк форм предоставляет вам гораздо больше возможностей. После понимания основ описанного выше процесса, вы должны быть готовы понять другие функции системы форм и будете готовы узнать немного больше о лежащей в основе машине.
Дополнительная информация о классах Django Form
Все классы форм создаются как подклассы 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 int и float. Вот как данные формы можно обработать в представлении, которое обрабатывает эту форму:
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>, на который ссылается сопровождающий тег label. Это важно для обеспечения доступности форм для вспомогательных технологий, таких как программы чтения с экрана. Вы также можете настроить способ генерации меток и идентификаторов.
См. Вывод форм в формате HTML для получения дополнительной информации об этом.
Ручное отображение полей
Нам не обязательно позволять Django распаковывать поля формы; мы можем сделать это вручную, если захотим (например, переупорядочить поля). Каждое поле доступно как атрибут формы с помощью {{ 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 }} - Имя поля, которое будет использоваться в атрибуте name элемента input. Оно учитывает префикс формы, если он был установлен.
-
{{ 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.10/topics/forms/index/