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

Вы уже поработали с моделями и формами, созданными на основе Generic Views. Чтобы увидеть полную картину — научимся обрабатывать формы во view-функциях.
В самом общем случае работа с формами происходит в таком порядке:
В Django есть полная инфраструктура для работы с формами:
Проще всего создать форму на основе существующей модели: достаточно написать класс формы, ссылающийся на модель. 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 автоматически подберёт особые виджеты формы:

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

Данные, переданные из форм, проверяются функциями-валидаторами. Валидатор обращается к данным (или получает их в качестве аргумента) и проверяет их значение. В случае ошибки вызывается исключение 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)
Валидация данных устроена так:
Скопировать код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])
Напишем валидатор, который заблокирует отправку формы, если в ней нет благодарности в адрес администратора сайта. Зачем нужно письмо, если в нём тебе не говорят «спасибо»?
Скопировать код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})