Проверка формы и полей ¶
Проверка формы происходит при очистке данных. Если вы хотите настроить этот процесс, есть разные места для внесения изменений, каждое из которых служит своей цели. Во время обработки формы используются три метода очистки. Обычно они выполняются, когда вы вызываете is_valid()
метод в форме. Есть и другие вещи, которые также могут запускать очистку и проверку (доступ к errors
атрибуту или full_clean()
прямой вызов ), но обычно они не нужны.
В общем, любой метод очистки может вызывать, ValidationError
если есть проблема с данными, которые он обрабатывает, передавая соответствующую информацию ValidationError
конструктору. См. Ниже
рекомендации по подъему ValidationError
. Если нет ValidationError
, метод должен возвращать очищенные (нормализованные) данные как объект Python.
Большую часть валидации можно выполнить с помощью валидаторов - помощников, которые можно использовать повторно. Валидаторы - это функции (или вызываемые объекты), которые принимают единственный аргумент и вызывают
ValidationError
недопустимый ввод. Валидаторы запускаются после вызова полей
to_python
и validate
методов.
Проверка формы разбита на несколько шагов, которые можно настроить или переопределить:
to_python()
Метод наField
первый шаг в каждой проверке. Он приводит значение к правильному типу данных и повышает,ValidationError
если это невозможно. Этот метод принимает исходное значение из виджета и возвращает преобразованное значение. Например, aFloatField
превратит данные в Pythonfloat
или подниметValidationError
.validate()
Способ поField
проверке ручки поля специфичной , что не подходит для проверки подлинности. Он принимает значение, которое было приведено к правильному типу данных, и возникаетValidationError
при любой ошибке. Этот метод ничего не возвращает и не должен изменять значение. Вы должны переопределить его, чтобы обрабатывать логику проверки, которую вы не можете или не хотите использовать в валидаторе.run_validators()
Метод наField
обегает валидатор и агрегатов всех ОШИБОК месторождения в одинValidationError
. Вам не нужно переопределять этот метод.clean()
Способ поField
подклассу отвечает за запускto_python()
,validate()
иrun_validators()
в правильном порядке и распространения их ошибки. Если в любое время какой-либо из методовValidationError
вызывает ошибку, проверка прекращается и возникает ошибка. Этот метод возвращает чистые данные, которые затем вставляются вcleaned_data
словарь формы.clean_<fieldname>()
Метод вызывается на форме подкласса - где<fieldname>
заменяется с именем атрибута поля формы. Этот метод выполняет любую очистку, специфичную для этого конкретного атрибута, независимо от типа поля. Этому методу не передаются никакие параметры. Вам нужно будет найти значение поля вself.cleaned_data
и помнить, что на данном этапе это будет объект Python, а не исходная строка, представленная в форме (она будет внутри,cleaned_data
потому что общийclean()
метод поля , описанный выше, уже очистил данные один раз).Например, если вы хотите проверить уникальность содержимого
CharField
вызываемого объектаserialnumber
,clean_serialnumber()
это будет правильное место для этого. Вам не нужно конкретное поле (это aCharField
), но вам нужна проверка, специфичная для поля формы, и, возможно, очистка / нормализация данных.Возвращаемое значение этого метода заменяет существующее значение в
cleaned_data
, поэтому оно должно быть значением поля изcleaned_data
(даже если этот метод не менял его) или новым очищенным значением.Метод подкласса формы
clean()
может выполнять проверку, требующую доступа к нескольким полям формы. Здесь вы можете поставить такие проверки, как «если полеA
указано, полеB
должно содержать действительный адрес электронной почты». Этот метод может при желании вернуть совершенно другой словарь, который будет использоваться какcleaned_data
.Поскольку к моменту вызова методы проверки поля были запущены
clean()
, у вас также есть доступ кerrors
атрибуту формы, который содержит все ошибки, возникающие при очистке отдельных полей.Обратите внимание, что любые ошибки, вызванные вашим
Form.clean()
переопределением, не будут связаны с каким-либо полем в частности. Они попадают в специальное «поле» (называемое__all__
), к которому вы можете получить доступ с помощьюnon_field_errors()
метода, если вам нужно. Если вы хотите прикрепить ошибки к определенному полю в форме, вам необходимо позвонитьadd_error()
.Также обратите внимание, что есть особые соображения при переопределении
clean()
методаModelForm
подкласса. ( дополнительную информацию см. в документации ModelForm )
Эти методы запускаются в указанном выше порядке, по одному полю за раз. То есть для каждого поля в форме (в том порядке, в котором они объявлены в определении формы) Field.clean()
, затем запускается метод (или его переопределение)
clean_<fieldname>()
. Наконец, как только эти два метода запускаются для каждого поля, Form.clean()
метод или его переопределение выполняется независимо от того, вызывали ли предыдущие методы ошибки или нет.
Примеры каждого из этих методов приведены ниже.
Как уже упоминалось, любой из этих методов может вызвать расширение ValidationError
. Для любого поля, если Field.clean()
метод вызывает a ValidationError
, никакой метод очистки для конкретного поля не вызывается. Однако методы очистки для всех остальных полей по-прежнему выполняются.
Повышение ValidationError
¶
Чтобы сообщения об ошибках были гибкими и легко отменяемыми, примите во внимание следующие рекомендации:
Предоставьте конструктору описательную ошибку
code
:# Good ValidationError(_('Invalid value'), code='invalid') # Bad ValidationError(_('Invalid value'))
Не вводите переменные в сообщение; используйте заполнители и
params
аргумент конструктора:# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError(_('Invalid value: %s') % value)
Используйте ключи сопоставления вместо позиционного форматирования. Это позволяет размещать переменные в любом порядке или вообще их опускать при перезаписи сообщения:
# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError( _('Invalid value: %s'), params=('42',), )
Оберните сообщение,
gettext
чтобы включить перевод:# Good ValidationError(_('Invalid value')) # Bad ValidationError('Invalid value')
Собираем все вместе:
raise ValidationError(
_('Invalid value: %(value)s'),
code='invalid',
params={'value': '42'},
)
Следование этим рекомендациям особенно необходимо при написании многоразовых форм, полей форм и полей модели.
Хотя это не рекомендуется, но если вы находитесь в конце цепочки проверки (то есть в вашем clean()
методе формы ) и знаете, что вам никогда не придется переопределять свое сообщение об ошибке, вы все равно можете выбрать менее подробный:
ValidationError(_('Invalid value: %s') % value)
Эти Form.errors.as_data()
и
Form.errors.as_json()
методы извлечь большую пользу из полнофункциональных ValidationError
с (с code
именем и params
словарем).
Возникновение множественных ошибок ¶
Если вы обнаружите несколько ошибок во время метода очистки и хотите сообщить обо всех их отправителю формы, можно передать список ошибок
ValidationError
конструктору.
Как и выше, рекомендуется передавать список ValidationError
экземпляров с помощью code
s, params
но список строк также будет работать:
# Good
raise ValidationError([
ValidationError(_('Error 1'), code='error1'),
ValidationError(_('Error 2'), code='error2'),
])
# Bad
raise ValidationError([
_('Error 1'),
_('Error 2'),
])
Использование валидации на практике ¶
В предыдущих разделах объяснялось, как в целом работает проверка форм. Так как иногда бывает проще установить вещи, наблюдая за каждой используемой функцией, вот серия небольших примеров, в которых используется каждая из предыдущих функций.
Использование валидаторов ¶
Поля формы (и модели) Django поддерживают использование служебных функций и классов, известных как валидаторы. Валидатор - это вызываемый объект или функция, которая принимает значение и ничего не возвращает, если значение допустимо, или вызывает a,
ValidationError
если нет. Их можно передать конструктору поля через validators
аргумент поля или определить в самом
Field
классе с помощью default_validators
атрибута.
Валидаторы можно использовать для проверки значений внутри поля, давайте посмотрим на Django SlugField
:
from django.core import validators
from django.forms import CharField
class SlugField(CharField):
default_validators = [validators.validate_slug]
Как видите, SlugField
это объект CharField
с настраиваемым валидатором, который проверяет, соответствует ли отправленный текст некоторым правилам символов. Это также можно сделать по определению поля:
slug = forms.SlugField()
эквивалентно:
slug = forms.CharField(validators=[validators.validate_slug])
Распространенные случаи, такие как проверка по электронной почте или регулярному выражению, можно обрабатывать с помощью существующих классов валидаторов, доступных в Django. Так , например,
validators.validate_slug
представляет собой экземпляр : RegexValidator
построен с первым аргументом является шаблоном: ^[-a-zA-Z0-9_]+$
. См. Раздел о
написании валидаторов, чтобы увидеть список того, что уже доступно, и пример того, как написать валидатор.
Очистка поля формы по умолчанию ¶
Давайте сначала создадим настраиваемое поле формы, которое проверяет, что его ввод - это строка, содержащая адреса электронной почты, разделенные запятыми. Полный класс выглядит так:
from django import forms
from django.core.validators import validate_email
class MultiEmailField(forms.Field):
def to_python(self, value):
"""Normalize data to a list of strings."""
# Return an empty list if no input was given.
if not value:
return []
return value.split(',')
def validate(self, value):
"""Check if value consists only of valid emails."""
# Use the parent's handling of required fields, etc.
super().validate(value)
for email in value:
validate_email(email)
Каждая форма, использующая это поле, будет запускать эти методы до того, как что-либо еще можно будет сделать с данными поля. Это очистка, характерная для данного типа поля, независимо от того, как оно впоследствии используется.
Давайте создадим, ContactForm
чтобы продемонстрировать, как вы будете использовать это поле:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
recipients = MultiEmailField()
cc_myself = forms.BooleanField(required=False)
Используйте MultiEmailField
как любое другое поле формы. Когда is_valid()
вызывается метод на форме, MultiEmailField.clean()
метод будет работать как часть процесса очистки , и это, в свою очередь, называют обычай
to_python()
и validate()
методы.
Очистка определенного атрибута поля ¶
Продолжая предыдущий пример, предположим, что в нашем ContactForm
мы хотим убедиться, что recipients
поле всегда содержит адрес
"[email protected]"
. Это проверка, специфичная для нашей формы, поэтому мы не хотим помещать ее в общий MultiEmailField
класс. Вместо этого мы пишем метод очистки, который работает с recipients
полем, например:
from django import forms
from django.core.exceptions import ValidationError
class ContactForm(forms.Form):
# Everything as before.
...
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "[email protected]" not in data:
raise ValidationError("You have forgotten about Fred!")
# Always return a value to use as the new cleaned data, even if
# this method didn't change it.
return data
Очистка и проверка полей, которые зависят друг от друга ¶
Предположим, мы добавляем еще одно требование в нашу контактную форму: если cc_myself
поле есть True
, оно subject
должно содержать слово "help"
. Мы выполняем проверку более чем одного поля за раз, поэтому метод формы
clean()
- хорошее место для этого. Обратите внимание, что здесь мы говорим о clean()
методе формы, тогда как раньше мы писали clean()
метод для поля. Когда вы решаете, где что-то проверять, важно сохранять четкость поля и различий в формах. Поля - это отдельные точки данных, формы - это набор полей.
К моменту вызова clean()
метода формы все отдельные методы очистки поля будут запущены (предыдущие два раздела), поэтому
self.cleaned_data
будут заполнены любыми данными, которые сохранились до сих пор. Таким образом, вам также необходимо помнить о том, что поля, которые вы хотите проверить, могли не пройти первоначальные проверки отдельных полей.
Есть два способа сообщить об ошибках на этом этапе. Вероятно, наиболее распространенный метод - отобразить ошибку в верхней части формы. Чтобы создать такую ошибку, вы можете поднять a ValidationError
из clean()
метода. Например:
from django import forms
from django.core.exceptions import ValidationError
class ContactForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = super().clean()
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject:
# Only do something if both fields are valid so far.
if "help" not in subject:
raise ValidationError(
"Did not send for 'help' in the subject despite "
"CC'ing yourself."
)
В этом коде, если возникает ошибка проверки, форма будет отображать сообщение об ошибке в верхней части формы (обычно) с описанием проблемы. Такие ошибки являются неполевыми ошибками, которые отображаются в шаблоне с помощью
.{{ form.non_field_errors }}
Вызов super().clean()
в примере кода обеспечивает сохранение любой логики проверки в родительских классах. Если ваша форма наследует другую форму, которая не возвращает cleaned_data
словарь в своем clean()
методе (это необязательно), тогда не назначайте cleaned_data
результат
super()
вызова и используйте self.cleaned_data
вместо этого:
def clean(self):
super().clean()
cc_myself = self.cleaned_data.get("cc_myself")
...
Второй подход к сообщению об ошибках проверки может включать присвоение сообщения об ошибке одному из полей. В этом случае давайте назначим сообщение об ошибке как строкам «subject», так и «cc_myself» в отображении формы. Будьте осторожны, делая это на практике, так как это может привести к путанице при выводе формы. Мы показываем, что здесь возможно, и оставляем вам и вашим дизайнерам решать, что эффективно работает в вашей конкретной ситуации. Наш новый код (заменяющий предыдущий образец) выглядит так:
from django import forms
class ContactForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = super().clean()
cc_myself = cleaned_data.get("cc_myself")
subject = cleaned_data.get("subject")
if cc_myself and subject and "help" not in subject:
msg = "Must put 'help' in subject when cc'ing yourself."
self.add_error('cc_myself', msg)
self.add_error('subject', msg)
Второй аргумент add_error()
может быть строкой или, предпочтительно, экземпляром ValidationError
. См. Дополнительные сведения в разделе « Повышение ошибки ValidationError» . Обратите внимание, что add_error()
автоматически удаляет поле из
cleaned_data
.