Валидация форм
Вы уже поработали с моделями и формами, созданными на основе 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
class PostForm(ModelForm):
class Meta:
model = Post
fields = ['pub_date', 'text']
form = PostForm()
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):
...
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):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
return redirect('/thank-you/')
return render(request, 'contact.html', {'form': form})
form = ContactForm()
return render(request, 'contact.html', {'form': form})