Чтобы зарегистрироваться на сайте, авторизоваться или опубликовать в блоге какое-то сообщение, пользователь должен отправить необходимую информацию на сервер.
Для этого были придуманы веб-формы — специальные компоненты HTML-страниц, в которых предусмотрены разнообразные поля ввода: текстовые поля, чекбоксы (поля с галочками), наборы radiobuttons (они дают возможность выбрать один вариант из нескольких).
HTML-форма может выглядеть так:
Пользователь заполняет поля, нажимает кнопку — и браузер пользователя отправляет введённые в форму данные на сервер.
А что внутри?
HTML-код этой формы:
<!-- Форма оборачивается тегом form,
в открывающем теге указывается тип и адрес запроса для отправки данных -->
<form method="post" action="/user-form/">
<!-- Тег label - заголовок для поля ввода -->
<label>Введите имя:</label>
<!-- type - тип поля ввода (здесь - текстовое поле),
name - имя поля в POST-запросе, в котором будет отправлена информация,
введённая пользователем -->
<input type="text" name="first_name">
<br><br>
<label>Введите фамилию:</label>
<input type="text" name="last_name">
<br><br>
<input type="checkbox" name="is_human"> Я человек
<br><br>
Опишите себя (выберите вариант)<br>
<input type="radio" name="quality" value="good"> Хороший<br>
<input type="radio" name="quality" value="bad"> Плохой<br>
<input type="radio" name="quality" value="ugly"> Злой
<br><br>
<!-- Этот код отрисует кнопку с надписью "Отправить" -->
<input type="submit" value="Отправить">
</form>
В открывающем теге формы указано, что после нажатия на кнопку «Отправить»
- данные будут отправлены POST-запросом:
method="post"
; - POST-запрос будет отправлен на адрес /user-form/:
action="/user-form/"
.
Названиями полей в POST-запросе будут значения атрибутов name
полей ввода формы, а значениями полей POST-запроса — данные, введённые или выбранные пользователем:
{'first_name': 'Клинт',
'last_name': 'Иствуд',
'is_human': True,
'quality': 'good'
}
На сервер придёт обычный POST-запрос, и его можно обработать: сохранить полученные данные в БД, проверить права доступа пользователя или сделать что-то ещё.
Точно так же будут работать формы регистрации, авторизации и любые другие: веб-форма — это инструмент для отправки запроса с веб-страницы.Отправка и обработка форм — востребованная задача, и в Django это процесс автоматизирован.
Объект формы
Для работы с веб-формами в Django есть несколько предустановленных классов, пользовательские классы наследуют от них. Классы Form и ModelForm упрощают создание веб-форм и обработку данных, полученных из них.
- объект класса формы можно передать в контекст страницы — и шаблонизатор автоматически сгенерирует веб-форму;
- при получении POST-запроса можно создать объект формы, передать в него данные запроса — и обработать их, используя встроенные методы класса формы.
Классы форм описывают в файле forms.py. Данные, полученные из веб-формы, могут обрабатываться по-разному: например, форма для публикации поста в блоге должна добавлять новую запись в БД, а форма для почтовой связи с другим пользователем сайта может просто отправлять письмо, не сохраняя данные в базу.
Самая востребованная задача при работе с формами — добавлять или редактировать информацию в базе данных. Для этого есть специальный класс форм: ModelForm.
Форма на основе модели: класс ModelForm
Экземпляр класса ModelForm можно напрямую связать с нужной моделью; форма автоматически возьмёт из модели поля и определит их свойства.
Если объект формы передать в контекст страницы — будет сгенерирована веб-форма с полями ввода, соответствующими полям модели.
При получении данных из веб-формы объект формы проверит полученную информацию и запишет её в связанную модель.Всё это умеет класс ModelForm: быстро, просто, надёжно — и без лишнего кода.В общем виде формы на основе моделей создаются так:
- Создаётся (или выбирается существующая) модель.
- Создаётся класс формы (наследуется от ModelForm), в нём указывается, с какой моделью будет работать эта форма.
- Объект формы (экземпляр, созданный на основе класса формы) передаётся в специальный view-класс.
- View-класс передаёт объект формы в шаблон, создаёт и возвращает пользователю страницу с веб-формой.
Для примера создадим форму для букиниста, который собирает в базу информацию о книгах.Первым делом — модель:
# library/models.py
from django.db import models
# Создадим модель, в которой будем хранить данные о книгах
class Book(models.Model):
name = models.CharField(max_length=200) # Название
isbn = models.CharField(max_length=100) # Индекс издания
pages = models.IntegerField(min_value=1) # Количество страниц
Создаём класс формы на основе этой модели; унаследуем его от ModelForm:
# library/forms.py
from django import forms # Импортируем модуль forms, из него возьмём класс ModelForm
from .models import Book # Импортируем модель, чтобы связать с ней форму
class BookForm(forms.ModelForm):
class Meta:
# Эта форма будет работать с моделью Book
model = Book
# Здесь перечислим поля модели, которые должны отображаться в веб-форме;
# при необходимости можно вывести в веб-форму только часть полей из модели.
fields = ('name', 'isbn', 'pages')
Готово. Теперь надо создать обработчик. Можно самостоятельно написать view-функцию, но Django не был бы Django, если бы в нём не был заготовлен механизм для обработки форм.
View-функции и view-классы
При обращении к какому-нибудь URL Django-проекта запрос передаётся в urls.py, и там специальный обработчик path()
вызывает view-объект, передавая ему в качестве аргумента объект типа request
, а на выходе ожидает объект типа response
.
Вызываемым объектом может быть view-функция, а может — специальный view-класс.
- View-функция, «функции представления» (от англ. view, «отображение, представление») или просто «представление». Это самый простой тип view-объектов. Функция получает на вход стандартный объект
request
и возвращает объект типаresponse
.Объектыresponse
могут быть созданы встроенными функциями-помощниками, например, функциейrender()
. Вызов view-функции:path('any_url/', my_view)
- View-классы или class-based view («представление, основанное на классе», «представление-класс»), как и view-функции, обрабатывают запрос и возвращают объект типа
response
. Такие классы должны наследоваться от специальных встроенных классов Generic Views*.*
Вpath()
view-классы вызываются через методas_view()
:path('any_url/', ClassName.as_view())
Generic Views
Generic Views (англ. «общий вид» или «базовое представление») — это встроенные в Django view-классы, созданные для решения стандартных задач. На основе Generic Views создают классы-наследники — view-классы, обладающие свойствами и методами родительского Generic Views; это классическое ООП в действии.Можно не писать собственный обработчик, а создать свой view-класс, унаследовав его от подходящего Generic View — и обработчик готов, остаётся вызвать его в urls.py.
View-класс для форм: CreateView
Для обработки формы BookForm
возьмём дженерик CreateView, он обрабатывает формы и на основе полученных из формы данных создаёт новые записи в БД.
# library/views.py
from django.views.generic.edit import CreateView
from .forms import BookForm
class BookView(CreateView): # Создаём свой класс, наследуем его от CreateView
# C какой формой будет работать этот view-класс
form_class = BookForm
# Какой шаблон применить для отображения веб-формы
template_name = 'library/new_book.html'
# Куда переадресовать пользователя после того, как он отправит форму
success_url = '/thankyou/'
В urls.py приложения опишем URL, по которому должна отображаться страница с формой и укажем обработчик для запросов к этому URL:
# library/urls.py
# теперь из файла urls.py для пути new_book/
# можно вызвать метод as_view() класса BookView
urlpatterns = [
# ...
path('new_book/', views.BookView.as_view(), name='new_book')
]
В зависимости от типа запроса view-класс BookView будет вести себя по-разному.
- При GET-запросе (например, пользователь в браузере перешёл по адресу new_book/)
- view-класс BookView получит запрос и передаст в шаблон library/new_book.html объект формы (экземпляр класса BookForm) с полями
'name'
,'isbn'
и'pages'
; - шаблонизатор сгенерирует HTML-код формы;
- веб-страница с формой будет отправлена пользователю.
- view-класс BookView получит запрос и передаст в шаблон library/new_book.html объект формы (экземпляр класса BookForm) с полями
- При POST-запросе (если пользователь заполнил веб-форму и нажал кнопку «Отправить»)
- POST-запрос приходит во view-класс BookView,
- данные запроса проверяются на соответствие требованиям модели (например, содержимое поля
name
не должно превышать 200 символов, а полеpages
должно быть числом, не меньшим единицы), - если проверка прошла успешно — будет создана новая запись в базе данных.
Шаблон для отрисовки формы
Осталось подготовить HTML-шаблон. View-класс передаёт в него объект формы form
, надо вывести форму на страницу.
Объект form
содержит список полей веб-формы, и тег шаблона {{ form.as_p }}
«обернёт» каждое поле в HTML-тег <p>
: в результате поля формы будут разбиты построчно, это будет выглядеть приличнее, чем если бы они склеились в одну строку.
<form method="post" action="{% url 'library:new_book' %}">
{{ form.as_p }}
<input type="submit" value="Отправить">
</form>
После рендеринга страницы получится такой HTML:
<form method="post" action="/new_book/">
<p><label for="id_name">name:</label>
<input id="id_name" type="text" name="name" maxlength="200" required></p>
<p><label for="id_isbn">isbn:</label>
<input id="id_isbn" type="text" name="isbn" maxlength="100" required></p>
<p><label for="id_pages">pages:</label>
<input id="id_pages" type="number" name="pages" required></p>
<input type="submit" value="Отправить">
</form>
Готово: в базу данных можно добавлять новые записи через веб-форму, размещённую на странице сайта.При отправке данных происходит примерно такая цепочка событий:GET-запрос к странице → заполнение HTML-формы → POST-запрос → обработка во view-классе → ORM → модель → БД