Файлы
practice
migrations
__init__.py
0001_initial.py
templates
index.html
thankyou.html
models.py
__init__.py
apps.py
forms.py
admin.py
tests.py
urls.py
views.py
task32forms
__init__.py
settings.py
urls.py
wsgi.py
manage.py
forms.py
Урок 15: Валидация форм
Доделайте сайт коллекционера компакт-дисков: создайте форму связи, через которую посетитель может предложить свой CD на обмен владельцу сайта.
Форма должна содержать такие поля:
  • Имя (name), текстовая строка, не более 100 символов.
  • Электронная почта для обратной связи (email)
  • Название альбома (title), текстовая строка, не более 100 символов.
  • Исполнитель (artist), текстовая строка, не более 40 символов.
  • Жанр (genre), поле выбора из предустановленных значений.
  • Стоимость (price), числовое поле, десятичные числа; необязательное.
  • Комментарий (comment), многострочное текстовое поле; необязательное.
Форма должна проверяться. Для обмена может быть допущен только диск, исполнитель которого уже есть в коллекции владельца сайта.
Форма должна быть доступна на главной странице. После заполнения данные формы надо отправить по электронной почте, а пользователь должен быть переадресован на страницу /thank-you.
Функция для отправки почты и шаблон для страницы /thank-you есть в коде.
Для проверки работы вам понадобится добавить несколько альбомов от имени администратора; для входа в систему используйте логин admin и пароль admin.
Браузер768x961
Браузер768x961

Валидация форм

Вы уже поработали с моделями и формами, созданными на основе Generic Views. Чтобы увидеть полную картину — научимся обрабатывать формы во view-функциях.
В самом общем случае работа с формами происходит в таком порядке:
  • Разработчик создал модель, на её основе создал форму, эту форму вывел в шаблон. Можно создать форму и без модели, если хранить полученные данные не нужно.
  • Пользователь заходит на страницу с формой, заполняет её и нажимает «Отправить», браузер пользователя отправляет POST-запрос с данными на URL, указанный в параметре action формы.
  • На сервере данные из формы проверяются и обрабатываются во view-функции.
  • Если данные успешно прошли проверку — они передаются для дальнейшей обработки и сохранения в базе, а пользователь перенаправляется на страницу с информацией об успешной отправке формы.
  • Если отправленные данные не прошли проверку, пользователю отправляется уведомление об ошибке.
В Django есть полная инфраструктура для работы с формами:
  • Формы Django можно связывать с объектами модели.
  • На основе моделей можно быстро, буквально несколькими строками кода, создавать новые формы.
  • Данные, полученные от пользователя, автоматически разбираются обработчиком запроса.
  • Django умеет передавать данные, полученные от пользователя, в объект формы.
  • У форм есть система валидации — проверки качества переданных данных. Пользователь может ошибиться или раньше времени нажать кнопку «Отправить» — в этих случаях данные не пройдут проверку и пользователь получит сообщение об этом.
  • Во все формы Django по умолчанию встроена защита от злонамеренной отправки данных из другого источника, Cross Site Request Forgery (CSRF).
  • В Django предусмотрен набор встроенных виджетов — шаблонов, формирующих HTML-код формы: поля для ввода текста (HTML-тег <input type="text">), поля многострочного ввода<textarea>, чекбоксы <input type="checkbox">, выпадающие списки <select> и множество других, стандартных и нестандартных, элементов.
Проще всего создать форму на основе существующей модели: достаточно написать класс формы, ссылающийся на модель. Django автоматически сопоставит имена и типы полей и подберёт стандартные виджеты для них:
Скопировать кодPYTHON
from django.forms import ModelForm from post.models import Post # cоздание класса формы class PostForm(ModelForm): class Meta: model = Post fields = ['pub_date', 'text'] # создание формы для отправки постов form = PostForm() # создание формы для редактирования конкретного объекта модели Post # запрашиваем объект post = Post.objects.get(pk=3) # передаём запрошенный объект в форму form = PostForm(instance=post)
Если форма привязана к определённому объекту модели, то в неё будут выведены данные этого объекта. В нашем примере в форму будет выведен пост с pk=3, его можно будет изменить и сохранить изменения.
Для определённых типов полей модели Django автоматически подберёт особые виджеты формы:
  • если в модели есть поле типа ForeignKey, то в форме будет отрисовано поле выбора ModelChoiceField, <select> для выбора объекта связанной модели;
  • для поля модели ManyToManyField в форме будет применён виджет ModelMultipleChoiceField, поле для множественного выбора из списка;
  • для поля модели SlugField в форме отобразится поле SlugField. Это специальное текстовое поле <input type="text"> умеет создавать красивые URL для объектов и проверять, что строка состоит только из разрешённых для URL символов. Например, для этого урока SlugField предложил бы URL /validatsiya-form

Валидация форм

Данные, переданные из форм, проверяются функциями-валидаторами. Валидатор обращается к данным (или получает их в качестве аргумента) и проверяет их значение. В случае ошибки вызывается исключение ValidationError. В Django есть множество встроенных валидаторов, но можно написать и собственный.
Валидация поля формы может быть многоэтапной. Значения, полученные после валидации каждого из полей, доступны в словаре form.cleaned_data. Ключи в этом словаре — названия полей формы.
Создадим форму для отправки сообщений администратору сайта:
Скопировать кодPYTHON
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)
Валидация данных устроена так:
  • Для любого поля модели или формы можно указать аргумент validators и в нём перечислить функции-валидаторы для этого поля. При получении данных валидаторы вызываются автоматически, им передаётся значение поля, и в случае ошибки валидации вызывается исключение ValidationError
Скопировать кодPYTHON
# функция-валидатор: def validate_positive(value): if value <= 0: raise forms.ValidationError( 'Значение меньше нуля', params={'value': value}, ) # можно указать валидатор для свойства (поля) модели class MyModel(models.Model): number_field = models.IntegerField(validators=[validate_positive]) # ...а можно указать валидатор для свойства (поля) формы class MyForm(forms.Form): number_field = forms.IntegerField(validators=[validate_positive])
  • В классе формы для каждого отдельного поля можно создать метод clean_<имя поля>, он вызовется автоматически во время валидации данных. Этот метод не получает никаких дополнительных параметров: он должен самостоятельно запросить значение из словаря cleaned_data. Такой протокол создан для того, чтобы можно было совместить работу всех валидаторов. Значение, которое вернёт этот метод, обновит значение соответствующего элемента словаря cleaned_data.
Напишем валидатор, который заблокирует отправку формы, если в ней нет благодарности в адрес администратора сайта. Зачем нужно письмо, если в нём тебе не говорят «спасибо»?
Скопировать кодPYTHON
class ContactForm(forms.Form): # форма обратной связи ... # метод-валидатор для поля subject def clean_subject(self): data = self.cleaned_data['subject'] # если пользователь не поблагодарил администратора - считаем это ошибкой if "спасибо" not in data.lower(): raise forms.ValidationError("Вы обязательно должны нас поблагодарить!") # метод-валидатор обязательно должен вернуть очищенные данные, даже если не изменил их return data
Классам полей формы и модели тоже можно присвоить методы валидации. Например, вы можете создать поле для ввода цвета ColorField с валидацией — и валидация этого поля будет работать во всех формах, где оно применяется.
Форма считается валидной, если метод формы .is_valid() возвращает True.
После валидации все данные будут сохранены в form.cleaned_data, и дальнейшая работа с информацией из формы идёт именно с этими данными:
Скопировать кодPYTHON
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']

Работа с пользовательскими данными

Создаём класс формы:
файл forms.py
Скопировать кодPYTHON
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)
Данные, отправленные клиентом на сервер, доступны в словаре request.POST. Создадим объект формы и передадим в него полученную информацию:
файл views.py
Скопировать кодPYTHON
from django.shortcuts import redirect def user_contact(request): # проверим, пришёл ли к нам POST-запрос или какой-то другой: if request.method == 'POST': # создаём объект формы класса ContactForm и передаём в него полученные данные form = ContactForm(request.POST) # проверяем данные на валидность: # ... здесь код валидации ... if form.is_valid(): # обрабатываем данные формы, используя значения словаря form.cleaned_data # возвращаем результат # Функция redirect перенаправляет пользователя # на другую страницу сайта, чтобы защититься # от повторного заполнения формы, если посетитель # сайта случайно перезагрузит страницу return redirect('/thank-you/') # если не сработало условие if form.is_valid() и данные не прошли валидацию # сработает следующий блок кода, # иначе команда return прервала бы дальнейшее исполнение функции # вернём пользователю страницу с HTML-формой и передадим полученный объект формы на страницу, # чтобы вернуть информацию об ошибке # заодно автоматически заполним прошедшими валидацию данными все поля, # чтобы не заставлять пользователя второй раз заполнять их return render(request, 'contact.html', {'form': form}) form = ContactForm() return render(request, 'contact.html', {'form': form})
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Новый файл
Новая папка
Удалить папку
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Новый файл
Новая папка
Удалить папку
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Новый файл
Новая папка
Удалить папку
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Удалить файл
Переименовать
Новый файл
Новая папка
Удалить папку
Переименовать
Удалить файл
Новый файл
Новая папка