Надо ли использовать cleaned_data при сохранении формы?
1. Объясните, пожалуйста, зачем нужен form.cleaned_data, если объект form уже заполнен из request.POST ?
2. В request.POST в итоге получаются безопасные данные для сохранения данных прямым вызовом метода save()?
3. Есть предположение что cleaned_data в данном случае может понадобиться например когда данные из формы нужно отправить по емэилу или сохранить в поля другой модели?
- Вопрос задан более трёх лет назад
- 1453 просмотра
Как обработать данные в form.cleaned_data используя ModelForm или создать кастомный валидатор? Django
Была мысль сделать несвязанную с моделью форму, но тогда не очень понятно, как передать во vews экземпляр текущего пользователя, для изменений. (для экземпляра ModelForm я указываю его как instance=request.user).
- Пробую написать кастомный валидатор для формы clean_fieldname:
class UserProfileEdit(forms.ModelForm): username = forms.CharField(required=False, max_length=100, label='Введите имя пользователя', help_text='максимум 150 символов', widget=forms.TextInput(attrs=)) email = forms.EmailField(required=False, label='Email', widget=forms.TextInput(attrs=)) class Meta: model = User fields = ('username', 'email') def clean_email(self): email = self.cleaned_data['email'] if not email: email == * как передать существующий email? * return email
Но не могу сообразить, как в него передать данные по текущему пользователю, например его текущий email?
Как обычно перед вопросом перелопатил кучу всего, но решения для проблемы в таком контексте не нашел.
Поэтому вопроса вырисовывается сразу три, буду благодарен за ответ на любой из них, но для понимания предметной области, было бы круто, понять как это реализовать с разных сторон:
Цель изменять данные «имя» и «email» текущего пользователя, меняя пустые значения формы на существующие данные пользователя:
- Если я меняю cleaned_data через вьюху (мб правильнее назвать это «контролер»?), то как передать cleaned_data в измененном виде, пользуюсь ModelForm?
- Как передать текущего пользователя во вьюху, чтобы использовать просто Form (форму не связанную с моделью, для ModelForm это просто instance=request.user)?
- Как реализовать кастомный валидатор для формы, думаю, туда нужно передать данные по текущему пользователю, но не понимаю как. (на мой взгляд это лучшее решение, для этого кастомные валидаторы и существуют же 😉
Модель для пользователя использую стандартную для django — User. Спасибо!
API форм ¶
Этот документ содержит подробные сведения об API форм Django. Сначала вам следует прочитать введение в работу с формами .
Связанные и несвязанные формы ¶
Form Экземпляр либо связан с набором данных, или несвязанным .
- Если он привязан к набору данных, он способен проверить эти данные и отобразить форму как HTML с данными, отображаемыми в HTML.
- Если он не привязан , он не может выполнить проверку (потому что нет данных для проверки!), Но он все равно может отображать пустую форму как HTML.
Чтобы создать несвязанный Form экземпляр, создайте экземпляр класса:
>>> f = ContactForm()
Чтобы привязать данные к форме, передайте данные в виде словаря в качестве первого параметра Form конструктору класса:
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True> >>> f = ContactForm(data)
В этом словаре ключи — это имена полей, которые соответствуют атрибутам в вашем Form классе. Значения — это данные, которые вы пытаетесь проверить. Обычно это строки, но не обязательно, чтобы они были строками; тип данных, которые вы передаете, зависит от Field , как мы увидим чуть позже.
Если вам нужно различать связанные и несвязанные экземпляры формы во время выполнения, проверьте значение is_bound атрибута формы :
>>> f = ContactForm() >>> f.is_bound False >>> f = ContactForm('subject': 'hello'>) >>> f.is_bound True
Обратите внимание, что передача пустого словаря создает связанную форму с пустыми данными:
>>> f = ContactForm(<>) >>> f.is_bound True
Если у вас есть связанный Form экземпляр и вы хотите как-то изменить данные, или если вы хотите привязать несвязанный Form экземпляр к некоторым данным, создайте другой Form экземпляр. Невозможно изменить данные в Form экземпляре. После создания Form экземпляра следует рассматривать его данные как неизменяемые, независимо от того, есть в нем данные или нет.
Использование форм для проверки данных ¶
Form. clean () ¶
Реализуйте clean() метод на своем, Form когда вы должны добавить настраиваемую проверку для взаимозависимых полей. См. Раздел Очистка и проверка полей, которые зависят друг от друга, например, использования.
Основная задача Form объекта — проверить данные. С привязанным Form экземпляром вызовите is_valid() метод, чтобы запустить проверку и вернуть логическое значение, указывающее, действительны ли данные:
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True> >>> f = ContactForm(data) >>> f.is_valid() True
Попробуем с некоторыми неверными данными. В этом случае subject поле пусто (ошибка, поскольку по умолчанию все поля обязательны) и sender не является действительным адресом электронной почты:
>>> data = 'subject': '', . 'message': 'Hi there', . 'sender': 'invalid email address', . 'cc_myself': True> >>> f = ContactForm(data) >>> f.is_valid() False
Form. errors ¶
Получите доступ к errors атрибуту, чтобы получить словарь сообщений об ошибках:
>>> f.errors
В этом словаре ключи — это имена полей, а значения — это списки строк, представляющих сообщения об ошибках. Сообщения об ошибках хранятся в списках, потому что в поле может быть несколько сообщений об ошибках.
Вы можете получить доступ errors без предварительного звонка is_valid() . Данные формы будут проверены при первом вызове is_valid() или доступе errors .
Процедуры проверки будут вызываться только один раз, независимо от того, сколько раз вы обращались errors или звонили is_valid() . Это означает, что если у валидации есть побочные эффекты, эти побочные эффекты будут срабатывать только один раз.
Возвращает, dict который сопоставляет поля с их исходными ValidationError экземплярами.
>>> f.errors.as_data() 'subject': [ValidationError(['This field is required.'])]>
Используйте этот метод в любое время, когда вам нужно определить ошибку по ее code . Это позволяет делать такие вещи, как переписывание сообщения об ошибке или написание пользовательской логики в представлении при наличии данной ошибки. Его также можно использовать для сериализации ошибок в настраиваемом формате (например, XML); например, as_json() полагается на as_data() .
Необходимость в as_data() методе обусловлена обратной совместимостью. Раньше ValidationError экземпляры терялись, как только их отображаемые сообщения об ошибках добавлялись в Form.errors словарь. В идеале Form.errors должны были быть сохраненные ValidationError экземпляры и методы с as_ префиксом, которые могли бы их отображать, но это нужно было сделать наоборот, чтобы не нарушить код, ожидающий отображаемых сообщений об ошибках в Form.errors .
Form.errors. as_json ( escape_html = Ложь ) ¶
Возвращает ошибки, сериализованные как JSON.
>>> f.errors.as_json() ], "subject": []>
По умолчанию as_json() не экранирует свой вывод. Если вы используете его для чего-то вроде запросов AJAX к представлению формы, где клиент интерпретирует ответ и вставляет ошибки на страницу, вы должны быть уверены, что избегаете результатов на стороне клиента, чтобы избежать возможности перекрестного -сайт скриптовая атака. Вы можете сделать это в JavaScript с помощью или с помощью jQuery (а не его функции). element.textContent = errorText $(el).text(errorText) .html()
Если по какой-то причине вы не хотите использовать экранирование на стороне клиента, вы также можете установить, escape_html=True и сообщения об ошибках будут экранированы, чтобы вы могли использовать их непосредственно в HTML.
Form.errors. get_json_data ( escape_html = Ложь ) ¶
Возвращает ошибки в виде словаря, подходящего для сериализации в JSON. Form.errors.as_json() возвращает сериализованный JSON, а это возвращает данные об ошибке перед сериализацией.
escape_html Параметр ведет себя , как описано в Form.errors.as_json() .
Form. add_error ( поле , ошибка ) ¶
Этот метод позволяет добавлять ошибки в определенные поля изнутри Form.clean() метода или вообще извне формы; например с точки зрения.
field Аргумент является именем поля , к которому следует добавить ошибки. Если его значение равно, None ошибка будет рассматриваться как ошибка, не связанная с полем, возвращенная функцией Form.non_field_errors() .
error Аргумент может быть строкой, или , предпочтительно , является экземпляром ValidationError . См. Раздел « Повышение ошибки ValidationError», чтобы узнать о лучших методах определения ошибок формы.
Обратите внимание, что Form.add_error() автоматически удаляет соответствующее поле из cleaned_data .
Form. has_error ( поле , код = Нет ) ¶
Этот метод возвращает логическое значение, указывающее, есть ли в поле ошибка с определенной ошибкой code . Если code есть None , он вернется, True если поле вообще содержит какие-либо ошибки.
Чтобы проверить наличие неполевых ошибок, используйте NON_FIELD_ERRORS в качестве field параметра.
Этот метод возвращает список ошибок Form.errors , не связанных с конкретным полем. Это включает в себя ValidationError s, которые возникают, Form.clean() и ошибки, добавленные с использованием . Form.add_error(None, «. «)
Поведение несвязанных форм ¶
Бессмысленно проверять форму без данных, но для записи вот что происходит с несвязанными формами:
>>> f = ContactForm() >>> f.is_valid() False >>> f.errors <>
Динамические начальные значения ¶
Form. initial ¶
Используется initial для объявления начального значения полей формы во время выполнения. Например, вы можете захотеть заполнить username поле именем пользователя текущего сеанса.
Для этого используйте initial аргумент для Form . Этот аргумент, если он задан, должен быть словарём, отображающим имена полей в начальные значения. Включите только те поля, для которых вы указываете начальное значение; необязательно включать все поля в форму. Например:
>>> f = ContactForm(initial='subject': 'Hi there!'>)
Эти значения отображаются только для несвязанных форм и не используются в качестве резервных значений, если не указано конкретное значение.
Если a Field определяет и вы включаете при создании экземпляра , то последний будет иметь приоритет. В этом примере предоставляется как на уровне поля, так и на уровне экземпляра формы, и последний получает приоритет: initial initial Form initial initial
>>> from django import forms >>> class CommentForm(forms.Form): . name = forms.CharField(initial='class') . url = forms.URLField() . comment = forms.CharField() >>> f = CommentForm(initial='name': 'instance'>, auto_id=False) >>> print(f)Name: Url: Comment:
Form. get_initial_for_field ( поле , имя_поля ) ¶
Используется get_initial_for_field() для получения исходных данных для поля формы. Он извлекает данные из Form.initial и Field.initial в указанном порядке и оценивает любые вызываемые начальные значения.
Проверка того, какие данные формы были изменены ¶
Form. has_changed () ¶
Используйте has_changed() метод на вашем, Form когда вам нужно проверить, изменились ли данные формы по сравнению с исходными данными.
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True> >>> f = ContactForm(data, initial=data) >>> f.has_changed() False
Когда форма отправлена, мы реконструируем ее и предоставляем исходные данные, чтобы можно было провести сравнение:
>>> f = ContactForm(request.POST, initial=data) >>> f.has_changed()
has_changed() будет, True если данные из request.POST отличаются от предоставленных initial или False иным образом. Результат вычисляется путем вызова Field.has_changed() каждого поля в форме.
changed_data Атрибут возвращает список имен полей , значения которых в связанном виде данных (обычно request.POST ) отличаются от того, что было предусмотрено в initial . Если данные не отличаются, он возвращает пустой список.
>>> f = ContactForm(request.POST, initial=data) >>> if f.has_changed(): . print("The following fields changed: %s" % ", ".join(f.changed_data)) >>> f.changed_data ['subject', 'message']
Доступ к полям из формы ¶
Form. fields ¶
Вы можете получить доступ к полям Form экземпляра из его fields атрибута:
>>> for row in f.fields.values(): print(row) . >>> f.fields[‘name’]
Вы можете изменить поле Form экземпляра, чтобы изменить способ его представления в форме:
>>> f.as_table().split('\n')[0] 'Name: ' >>> f.fields['name'].label = "Username" >>> f.as_table().split('\n')[0] 'Username: '
Остерегайтесь изменять base_fields атрибут, потому что это изменение повлияет на все последующие ContactForm экземпляры в том же процессе Python:
>>> f.base_fields['name'].label = "Username" >>> another_f = CommentForm(auto_id=False) >>> another_f.as_table().split('\n')[0] 'Username: '
Доступ к «чистым» данным ¶
Form. cleaned_data ¶
Каждое поле в Form классе отвечает не только за проверку данных, но и за их «очистку» — приведение их к согласованному формату. Это приятная функция, потому что она позволяет вводить данные для определенного поля различными способами, всегда приводя к согласованному выводу.
Например, DateField нормализует ввод в datetime.date объект Python . Независимо от того, передаете ли вы ему строку в формате ‘1994-07-15’ , datetime.date объекте или ряде других форматов, DateField она всегда будет нормализовать ее до datetime.date объекта, пока она действительна.
После того, как вы создали Form экземпляр с набором данных и проверили его, вы можете получить доступ к чистым данным через их cleaned_data атрибут:
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True> >>> f = ContactForm(data) >>> f.is_valid() True >>> f.cleaned_data [email protected]', 'subject': 'hello'>
Обратите внимание, что любое текстовое поле, такое как CharField или, EmailField всегда очищает ввод до строки. Мы рассмотрим последствия кодирования позже в этом документе.
Если ваши данные не проходят проверку, cleaned_data словарь содержит только допустимые поля:
>>> data = ‘subject’: », . ‘message’: ‘Hi there’, . ‘sender’: ‘invalid email address’, . ‘cc_myself’: True> >>> f = ContactForm(data) >>> f.is_valid() False >>> f.cleaned_data
cleaned_data всегда будет содержать только ключ для полей, определенных в Form , даже если вы передадите дополнительные данные при определении Form . В этом примере мы передаем конструктору несколько дополнительных полей ContactForm , но они cleaned_data содержат только поля формы:
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True, . 'extra_field_1': 'foo', . 'extra_field_2': 'bar', . 'extra_field_3': 'baz'> >>> f = ContactForm(data) >>> f.is_valid() True >>> f.cleaned_data # Doesn't contain extra_field_1, etc. [email protected]', 'subject': 'hello'>
Когда Form является допустимым, cleaned_data будет включать ключ и значение для всех своих полей, даже если данные не содержат значения для некоторых дополнительных полей. В этом примере словарь данных не включает значение для nick_name поля, но cleaned_data включает его с пустым значением:
>>> from django import forms >>> class OptionalPersonForm(forms.Form): . first_name = forms.CharField() . last_name = forms.CharField() . nick_name = forms.CharField(required=False) >>> data = ‘first_name’: ‘John’, ‘last_name’: ‘Lennon’> >>> f = OptionalPersonForm(data) >>> f.is_valid() True >>> f.cleaned_data
В приведенном выше примере cleaned_data значение для nick_name установлено в пустую строку, потому что nick_name is CharField и CharField s обрабатывают пустые значения как пустую строку. Каждому типу поля известно, какое у него «пустое» значение — например, для DateField него None вместо пустой строки. Для получения полной информации о поведении каждого поля в этом случае см. Примечание «Пустое значение» для каждого поля в разделе «Встроенные Field классы» ниже.
Вы можете написать код для выполнения проверки для определенных полей формы (на основе их имени) или для формы в целом (с учетом комбинаций различных полей). Дополнительные сведения об этом см. В разделе Проверка форм и полей .
Вывод форм в формате HTML ¶
Вторая задача Form объекта — отобразить себя как HTML. Для этого print он:
>>> f = ContactForm() >>> print(f)
Если форма привязана к данным, вывод HTML будет включать эти данные соответствующим образом. Например, если поле представлено символом , данные будут в атрибуте. Если поле представлено значком , этот HTML-код будет включать, если необходимо: value checked
>>> data = 'subject': 'hello', . 'message': 'Hi there', . 'sender': '[email protected]', . 'cc_myself': True> >>> f = ContactForm(data) >>> print(f)[email protected]" required>
- Для гибкости, выход вовсе не включает в себя
и
тег, ни он Включен ли
метку или тег. Это твоя работа.
- Каждый тип поля имеет представление HTML по умолчанию. CharField обозначается символами an и an . представлен расширением . Обратите внимание, что это просто разумные значения по умолчанию; вы можете указать, какой HTML использовать для данного поля, используя виджеты, которые мы вскоре объясним. EmailField BooleanField(null=False)
- HTML name для каждого тега берется непосредственно из имени его атрибута в ContactForm классе.
- Текстовая метка для каждого поля — например ‘Subject:’ , ‘Message:’ и создается из имени поля путем преобразования всех подчеркиваний в пробелы и верхнего регистра первой буквы. Опять же, обратите внимание, что это просто разумные значения по умолчанию; вы также можете указать метки вручную. ‘Cc myself:’
- Каждая текстовая метка окружена тегом HTML , который указывает на соответствующее поле формы через свой id . Его id , в свою очередь, генерируется путем добавления ‘id_’ к имени поля. Эти id атрибуты и теги включены в выводе по умолчанию, чтобы следовать рекомендациям, но вы можете изменить это поведение.
- В выводе используется синтаксис HTML5, таргетинг . Например, он использует логические атрибуты, такие как, а не стиль XHTML . checked checked=’checked’
as_p() ¶
as_p() отображает форму как серию
тегов, каждый
из которых содержит одно поле:
>>> f = ContactForm() >>> f.as_p() '\n
\n
\n
'
>>> print(f.as_p())
Проверка форм и полей формы¶
Проверка формы происходит при нормализации её данных. При возникновении необходимости вмешаться в этот процесс, есть много мест, где можно это сделать и которые влияют на разные этапы проверки. Во время обработки формы вызываются три типа методов для нормализации данных. Процесс проверки запускается при вызове метода is_valid() формы. Существуют ситуации, которые запускают нормализацию и проверку данных (обращение к свойству errors или прямой вызов метода full_clean() ), но они возникают достаточно редко.
В общем случае, любой нормализующий метод может вызвать исключение ValidationError при наличии проблем с данными, передавая соответствующее сообщение об ошибке в конструктор исключения. Смотрите ниже примеры, как правильно вызывать ValidationError . Если проблем не выявлено, то метод должен возвращать нормализованное значение в виде объекта языка Python.
Большая часть проверок может быть выполнена с помощью validators, которые являются простыми в использовании вспомогательными объектами. Валидатор — это простая функция (или вызываемый объект, callable), которая принимает единственный аргумент и вызывает исключение ValidationError в случае проблем с полученным значением. Валидаторы запускаются после вызова методов поля: to_python и validate .
Проверка формы состоит из нескольких этапов, каждый из которых может быть настроен или переопределён:
- Вызов метода поля to_python() является первым этапом каждой проверки. Он приводит значение к соответствующему типу данных или вызывает исключение ValidationError , если это невозможно. Метод принимает сырое значение от виджета и возвращает нормализованное значение. Например, поле типа FloatField преобразовывает данные в тип float языка Python или вызывает исключение ValidationError .
- Метод validate() поля выполняет специфическую для поля проверку данных и приводит значение к правильному типу данных, или вызывает исключение ValidationError на любую ошибку. Этот метод не возвращает значение и не должен изменять проверяемые данные. Если вам надо обеспечить логику, которую невозможно или нежелательно выносить в валидатор, то вам следует переопределить этот метод.
- Метод поля run_validators() запускает все валидаторы и аккумулирует все возникающие ошибки в одно исключение ValidationError . Вам не стоит переопределять этот метод.
- Метод clean() поля отвечает за вызов методов to_python() , validate() и run_validators() в правильном порядке и передачу их ошибок. Как только любой из этих методов вызовет исключение ValidationError , процесс проверки прекращается и ошибка передаётся выше. Этот метод возвращает проверенные данные, которые затем помещаются в словарь cleaned_data формы.
- Для проверки значения поля используется метод clean_() , где заменяется на имя поля. Этот метод выполняет проверку значения. Метод не принимает аргументы. Для получения значения поля обращайтесь к словарю self.cleaned_data и помните, что там будет объект языка Python, а не строка, переданная формой (значение находится в cleaned_data т.к. уже была выполнена проверка методом clean() поля). Например, если требуется проверить, что содержимое CharField поля с именем serialnumber является уникальным, то метод clean_serialnumber() будет правильным местом для такого функционала. Вам не нужно специальное поле (пусть будет CharField ), но требуется хитрая проверка данных и, возможно, очистка/нормализация данных. Этот метод должен возвращать очищенное значение, полученное из cleaned_data независимо, изменилось оно или нет.
- Метод clean() потомка формы. Этот метод может выполнять любую проверку, которая нуждается в одновременном доступе к данным нескольких полей. Именно здесь вы можете проверять, что если поле A заполнено, то поле B должно содержать правильный адрес электронной почты и так далее. Данные, которые возвращает этот метод, помещаются в свойство cleaned_data формы. Так как валидация полей выполняется перед вызовом clean() , вы можете получить доступ к атрибуту формы errors , который содержит уже полученные ошибки валидации. Следует отметить, что любая ошибка, вызванная методом Form.clean() формы, не будет ассоциирована ни с каким полем. Такие ошибки привязываются к «особому» полю ( __all__ ), доступ к которому можно получить через метод non_field_errors(). Если вам потребуется добавить ошибки к определённому полю формы, используйте add_error(). Также следует отметить, что существует ряд соглашений, которым необходимо следовать при переопределении метода clean() в вашем классе ModelForm . (Обратитесь к документации на ModelForm для получения подробностей.)
Эти методы вызываются в порядке, указанном выше, по одному полю за раз. Для каждого поля формы (в порядке их определения в классе формы) вызывается сначала метод Field.clean() , затем вызывается метод clean_() . После того, как пара этих методов будет вызвана для каждого поля формы, наступает очередь метода Form.clean() формы. Он будет вызыван в любом случае, даже если предыдущие методы вызывали ошибку.
Примеры для каждого из этих методов показаны ниже.
Как упоминалось ранее, любой из этих методов может вызвать исключение ValidationError . Для любого поля, если его метод clean() вызвал исключение ValidationError , то следующий метод для этого поля не вызывается. Тем не менее, методы для остальных полей отрабатывают в штатном режиме.
Вызов ValidationError ¶
Для удобной работы с ошибками валидации используйте следующие правила:
Передайте при создании код ошибки через аргумент code :
# Good ValidationError(_('Invalid value'), code='invalid') # Bad ValidationError(_('Invalid value'))# 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',), )# 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)Вызов нескольких ошибок¶
При обнаружении нескольких ошибок в процессе нормализации поля и при наличии желания отобразить их одновременно на форме, следует передать их в виде списка в конструктор исключения.
Рекомендуется использовать список объектов ValidationError с code и params , но можно использовать просто список строк:
# Good raise ValidationError([ ValidationError(_('Error 1'), code='error1'), ValidationError(_('Error 2'), code='error2'), ]) # Bad raise ValidationError([ _('Error 1'), _('Error 2'), ])Использование проверки на практике¶
Выше мы рассмотрели как осуществляется проверка форм в целом. Так как временами бывает проще разобраться с функционалом, просмотрев его в действии, далее показан ряд небольших примеров, которые используют описанные возможности.
Использование валидаторов¶
Поля форм (и моделей) Django поддерживают использование простых функций и классов, которые известны как валидаторы. Это просто функция, которая принимает значение и ничего не возвращает, если значение верно, иначе вызывает ValidationError . Они могут быть переданы в конструктор поля через аргумент validators или определены в самом классе поля Field с помощью атрибута default_validators .
Простые валидаторы могут использоваться для проверки значений внутри полей. Давайте рассмотрим SlugField :
from django.forms import CharField from django.core import validators class SlugField(CharField): default_validators = [validators.validate_slug]Как можно увидеть SlugField — это обычное поле CharField , которое имеет валидатор, проверяющий вводимое значение на допустимые символы. Все это можно указать при определении поля:
slug = forms.SlugField()slug = forms.CharField(validators=[validators.validate_slug])Обычные проверки, такие как проверка email или по регулярному выражению, можно выполнить используя существующие валидаторы 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(MultiEmailField, self).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 всегда должно содержать адрес "fred@example.com" . Эта проверка будет особенностью нашей формы, следовательно, нам не надо её помещать в класс MultiEmailField . Вместо этого мы напишем метод, который будет проверять поле recipients :
from django import forms class ContactForm(forms.Form): # Everything as before. . def clean_recipients(self): data = self.cleaned_data['recipients'] if "fred@example.com" not in data: raise forms.ValidationError("You have forgotten about Fred!") # Always return the cleaned data, whether you have changed it or # not. return dataОчистка и проверка полей, которые зависят друг от друга¶
Допустим, что мы добавили ещё одно требование для нашей формы: если поле cc_myself равно True , то поле subject должно содержать слово "help" . Раз мы выполняем проверку нескольких полей, то метод формы clean() будет правильным местом для нашего кода. Обратите внимание, мы сейчас говорим о методе clean() формы, а раньше говорили о методе clean() поля. Важно понимать разницу между ними при реализации алгоритма проверки данных. Поля содержат один источник данных, а формы — это коллекции полей.
К моменту вызова метода формы clean() все clean() методы полей уже отработали. Таким образом, свойство формы self.cleaned_data будет заполнено данными, прошедшими проверку. Следовательно, надо принять во внимание возможность того, что данные некоторых полей не прошли начальную поверку.
Существует два способа сообщить об ошибках на этом этапе. Обычно ошибку отображают сверху формы. Для этого достаточно вызвать исключение ValidationError в методе формы clean() . Например:
from django import forms class ContactForm(forms.Form): # Everything as before. . def clean(self): cleaned_data = super(ContactForm, self).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 forms.ValidationError( "Did not send for 'help' in the subject despite " "CC'ing yourself." )В данном коде, при возникновении ошибки во время проверки данных, форма отобразит сообщение об ошибке сверху (обычное поведение), описывая проблему.
Вызов super(ContactForm, self).clean() обеспечивает проверку данных в родительском классе. Если ваша форма наследуется от класса, который не возвращает словарь cleaned_data из метода clean() (это не обязательно), не записывайте в cleaned_data результат вызова super() и используйте вместо этого self.cleaned_data :
def clean(self): super(ContactForm, self).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(ContactForm, self).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 .