Курс по Django: https://stepik.org/a/183363
Архив проекта: 44_sitewomen.zip
Итак, у нас все
готово для создания нашей первой формы. Для этого в Django существует
специальный класс Form, на базе которого удобно создавать формы, не
связанные с моделями:
https://docs.djangoproject.com/en/4.2/ref/forms/api/#django.forms.Form
Где следует
объявлять формы? Обычно, для этого создают в приложении отдельный файл forms.py. Мы так и
сделаем (создаем файл women/forms.py). И в этом
файле импортируем пакет forms и наши модели:
from django import forms
from .models import Category, Husband
Следующий шаг – объявить класс AddPostForm, описывающий форму добавления статьи. Он будет
унаследован от базового класса Form и иметь следующий вид:
class AddPostForm(forms.Form):
title = forms.CharField(max_length=255)
slug = forms.SlugField(max_length=255)
content = forms.CharField(widget=forms.Textarea())
is_published = forms.BooleanField()
cat = forms.ModelChoiceField(queryset=Category.objects.all())
husband = forms.ModelChoiceField(queryset=Husband.objects.all())
Смотрите, мы
здесь определяем только те поля, с которыми будет взаимодействовать
пользователь. Например, поля модели time_create или time_update нигде не
фигурируют, так как заполняются автоматически. Далее, каждый атрибут формы
лучше назвать так же, как называются поля в таблице women. Впоследствии
нам это облегчит написание кода.
В классе формы каждый
атрибут – это ссылка на тот или иной экземпляр класса из пакета forms. Например, title определен через
класс CharField, поле is_published – через BooleanField, а список
категорий cat – через класс ModelChoiceField, который
формирует выпадающий список из данных, прочитанных из таблицы Category.
Почему указаны
именно такие классы? И какие классы вообще существуют для формирования полей
формы? Полный их список и назначения можно посмотреть на следующей странице
документации:
https://docs.djangoproject.com/en/4.2/ref/forms/fields/
Я советую вам в
целом изучить его и знать, как создавать различные типы полей. В частности,
класс CharField служит для
создания обычного текстового поля ввода, класс BooleanField – для checkbox’а, класс ModelChoiceField – списка с
данными из указанной модели.
Отображение формы в шаблоне
После того, как
форма определена, ее можно использовать в функции представления addpage(). В самом
простом варианте можно записать так:
def addpage(request):
form = AddPostForm()
return render(request, 'women/addpage.html', {'menu': menu, 'title': 'Добавление статьи', 'form': form})
Здесь создается
экземпляр формы и через переменную form передается
шаблону addpage.html. Осталось
отобразить форму в нашем шаблоне. Перейдем в файл addpage.html и пропишем там
следующие строчки:
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Добавить</button>
</form>
Здесь в теге <form> через
атрибут action в качестве
обработчика указана текущая страница (пустые кавычки). Атрибут method определяет способ
передачи информации на сервер (используется POST-запрос). В этом
случае внутри формы обязательно записываем специальный тег csrf_token, который
генерирует скрытое поле с уникальным токеном. Как я уже отмечал, это необходимо
для защиты от CSRF-атак, когда вредоносный
сайт пытается отправить данные от имени авторизованного пользователя. Фреймворк
Django не станет
обрабатывать данные, если отсутствует или не совпадает csrf-токен и, тем
самым, защищает пользователя от подобных атак.
Следующая
строчка {{ form.as_p }} вызывает
метод as_p объекта формы
для отображения ее полей с тегами абзацев <p>. Существуют
и другие методы, которые формируют поля в виде элементов списка <ul> или в виде
таблицы. Последний вариант, хоть и возможен, но считается устаревшей практикой.
Здесь также стоит иметь в виду, что по умолчанию все поля в Django обязательны,
если не указано обратное через параметр required=False.
Наконец,
последняя строчка – тег <button> создает кнопку типа submit для отправки
данных формы на сервер и, в конечном итоге, нашей функции представления addpage().
Если теперь
обновить страницу, то увидим все указанные поля формы со списком и кнопкой. Заполним
их, оставив флажок is_published выключенным, и
попробуем отправить данные. Браузер скажет нам, что поле is_published является
обязательным. Это не то поведение, которое нам нужно. Давайте все
необязательные поля отметим, как необязательные. Для этого в классе AddPostForm
у таких атрибутов пропишем параметр required=False:
class AddPostForm(forms.Form):
title = forms.CharField(max_length=255)
slug = forms.SlugField(max_length=255)
content = forms.CharField(widget=forms.Textarea(), required=False)
is_published = forms.BooleanField(required=False)
cat = forms.ModelChoiceField(queryset=Category.objects.all())
husband = forms.ModelChoiceField(queryset=Husband.objects.all(), required=False)
Все, теперь у
нас три обязательных поля: title, slug и cat, остальные
можно оставлять пустыми.
Обработка данных формы
Форма в
простейшем варианте у нас с вами готова. Конечно, она пока выглядит неказисто,
зато обладает нужным функционалом. Давайте теперь посмотрим, как можно
обработать данные этой формы на стороне сервера, то есть в функции
представления addpage().
Чтобы лучше
понять, что мы сейчас будем делать, я покажу данные, которые приходят в
функцию, при отправке формы. Поставим в addpage() точку
останова, запустим веб-сервер в режиме отладки и просто откроем страницу
«Добавить статью». Отобразим содержимое объекта request и видим, что в
нем словари GET и POST пустые, а
атрибут method принимает
значение ‘GET’ (в виде
строки). Давайте теперь заполним форму данным и снова отправим ее на сервер. Снова
смотрим содержимое объекта request и видим, что словарь POST заполнен
переданными данными, а атрибут method соответствует
строке ‘POST’. Именно по
этому методу мы передаем данные из формы на сервер.
Всей этой
информацией мы сейчас воспользуемся для обработки данных из формы. И определим
функцию addpost()
следующим
образом:
def addpage(request):
if request.method == 'POST':
form = AddPostForm(request.POST)
if form.is_valid():
print(form.cleaned_data)
else:
form = AddPostForm()
return render(request, 'women/addpage.html', {'menu': menu, 'title': 'Добавление статьи', 'form': form})
Как это работает?
Смотрите, вначале приходит обычный GET-запрос от браузера для открытии
страницы по маршруту:
http://127.0.0.1:8000/addpage/
В итоге, условие
не срабатывает и создается форма с пустыми полями, которая передается в шаблон
и он, затем, отображается в браузере. Именно поэтому мы видим форму без данных.
Далее, пользователь заполняет ее поля и отправляет на сервер по POST-запросу. Снова
срабатывает наша функция представления addpost() и на этот раз
проверка проходит. Формируется объект формы с переданными данными и вызывается
метод формы is_valid(), который
проверяет поля на корректность заполнения. Если проверка прошла, то в консоли
отобразится словарь form.cleaned_data полученных
данных от пользователя. Если же проверка не пройдет, то пользователь увидит сообщение
об ошибке.
Давайте запустим
веб-сервер, заполним нашу форму и нажмем на кнопку «Добавить». Если все заполнено
корректно, то метод is_valid() вернет True и в консоли
отобразятся, так называемые, очищенные данные (cleaned_data). При этом
страница перезагрузится, но введенные данные не пропадут, так как мы в шаблон
передаем объект form с заполненными полями, а не пустую.
Если же
заполнить форму с ошибками, например в поле slug указать русские
символы, то фреймворк Django автоматически сформирует
сообщение об ошибке, что в поле slug присутствуют
недопустимые символы. Соответственно, метод is_valid() в этом случае
вернет False и коллекция cleaned_data не будет
отображена в консоли. Видите, как это удобно? Нам самим даже не нужно делать
обработку типовых ошибок. Фреймворк Django все берет на
себя.
На следующем
занятии мы продолжим эту тему и сделаем улучшение внешнего вида формы, а также
сохранение переданных данных в БД.
Курс по Django: https://stepik.org/a/183363