Как переопределить pre_validate и post_validate в wtforms?

Я хочу создать форму с WTForms, где поле представляет собой SelectField со специальной (настраиваемой) проверкой, которая (например) проверяет что-то еще, если текущее значение недействительно (т.е. это не экземпляр определенных параметров).

Я прочитал в документацию, что правильный способ сделать это переопределить функции pre_validate или post_validate в соответствии с потребностями.

Итак, мой подход следующий:

1 - я создаю новое поле с именем MySelectForm:

class MySelectField(SelectField):
    def post_validate(self, form, validation_stopped):
        """overrides post validation"""
        #Here is my custom validation

2- Я использую это поле вместо исходного в своей форме

Чего я не могу понять, так это того, что если, как я читал в документах, post_validate принимает на вход «форму, которой принадлежит поле», как мне получить доступ к текущему значению поля, которое является экземпляром MySelectField?

Другими словами, может ли кто-нибудь привести пример того, как переопределить pre_validate или post_validate?


person Giovanni Di Milia    schedule 06.08.2013    source источник


Ответы (2)


Вы передаете self методу, и поэтому у вас есть доступный объект поля. Он наследуется от класса Field WTForms, поэтому у вас должен быть доступен field.data. Он содержит "результирующее (очищенное) значение вызова любой из методов обработки."

person Johannes Gehrs    schedule 07.08.2013
comment
Он попросил пример. - person polandeer; 25.08.2013

ниже часть исходного кода

# wtforms.fields.core.py  |  version: Flask-WTF 0.14.3

class Field(object):
    errors = tuple()
    ...

    def validate(self, form, extra_validators=tuple()):
        self.errors = list(self.process_errors)
        stop_validation = False

        self.check_validators(extra_validators)

        try:
            self.pre_validate(form)   # <---------- focus here
        except StopValidation as e:
            if e.args and e.args[0]:
                self.errors.append(e.args[0])
            stop_validation = True
        except ValueError as e:
            self.errors.append(e.args[0])

        # Run validators
        if not stop_validation:
            chain = itertools.chain(self.validators, extra_validators)
            stop_validation = self._run_validation_chain(form, chain)  # <-- It will run ``InputRequired, YourValidators1, YourValidators2, validate_username``  (see the example code)

        # Call post_validate
        try:
            self.post_validate(form, stop_validation)  # <---------- focus here
        except ValueError as e:
            self.errors.append(e.args[0])

        return len(self.errors) == 0

и я приведу вам пример post_validate (pre_validate очень похож, и я думаю, что вы можете сделать это самостоятельно)

from flask_wtf import FlaskForm, Form
from wtforms import StringField, ValidationError, StopValidation
from wtforms.validators import InputRequired
from typing import List


class StateStringField(StringField):
    ok_msg_list: List = None  # Because class ``wtforms.fields.core.Field`` are not existed any attributes I like, so I create one.

    def post_validate(self, form: Form, stop_validation: bool):
        if self.ok_msg_list is None:
            self.ok_msg_list = []
        if stop_validation:
            return
        if not 'errors':  # assume no errors...
            raise ValueError('...')
        else:
            self.ok_msg_list.append('OK')


class UserForm(FlaskForm):
    username = StateStringField('User',
                                validators=[InputRequired(),
                                            # YourValidators2, YourValidators3
                                            ])

    def validate_username(self, field: StringField):
        if len(field.data) > 50:
            raise StopValidation('Name must be less than 50 characters')
<!-- templates/auth/register.html-->
<form method="post">   
    {{ form.hidden_tag() }}
    <p>{{ form.username.label }} {{ form.username()|safe }}
      {% if form.username.errors %}
        {% for error in form.username.errors %}
          <p style="color:red;">{{ error }}<p>
        {% endfor %}
      {% endif %}

      {% if form.username['ok_msg_list'] %}  <!-- similar as python: if hasattr('ok_msg_list') -->
        {% for ok_msg in form.username.ok_msg_list %}
          <span style="color:blue;">{{ ok_msg|safe }}</span>
        {% endfor %}
      {% endif %}
    </p>
</form>

Резюме

Вы можете использовать post_validate, когда хотите выполнить еще одну проверку после того, как были выполнены все проверки. (Лично я думаю, что post_validate похож на finally.

В моем примере, когда пользователь проверяет все правильность проверки, рядом с ним появляется сообщение OK.

введите здесь описание изображения

person Carson    schedule 12.11.2020