Как сделать поле условно необязательным в WTForms? - PullRequest
26 голосов
/ 11 декабря 2011

Моя проверка формы почти завершена, у меня есть только 2 случая, которые я точно не знаю, как решить: 1) Конечно, необходимо указать поле пароля, но я также предоставляю возможность войти в систему с помощью учетной записи Google или Facebook. через OAuth, после чего имя заполняется, но я полностью удаляю поле пароля из формы, если есть пользователь (google) или пользовательский объект facebook:

<tr><td>
  <br />        {% if user or current_user %}    {% else %} 

  <div class="labelform">
     {% filter capitalize %}{% trans %}password{% endtrans %}{% endfilter %}:
  </div>
      </td><td>  <div class="adinput">{{ form.password|safe }}{% trans %}Choose a password{% endtrans %}</div>{% endif %}

  </td></tr>

Так что для тех пользователей, которые уже вошли в систему и поле пароля не имеет значения, мне нужна логика, чтобы сделать это поле условно необязательным. Я думал, что мог бы иметь переменную для logged_in + метод в моем классе формы, такой как это:

class AdForm(Form):
    logged_in = False
    my_choices = [('1', _('VEHICLES')), ('2', _('Cars')), ('3', _('Bicycles'))]
    name = TextField(_('Name'), [validators.Required(message=_('Name is required'))], widget=MyTextInput())
    title = TextField(_('title'), [validators.Required(message=_('Subject is required'))], widget=MyTextInput())
    text = TextAreaField(_('Text'),[validators.Required(message=_('Text is required'))], widget=MyTextArea())
    phonenumber = TextField(_('Phone number'))
    phoneview = BooleanField(_('Display phone number on site'))
    price = TextField(_('Price'),[validators.Regexp('\d', message=_('This is not an integer number, please see the example and try again')),validators.Optional()] )
    password = PasswordField(_('Password'),[validators.Optional()], widget=PasswordInput())
    email = TextField(_('Email'), [validators.Required(message=_('Email is required')), validators.Email(message=_('Your email is invalid'))], widget=MyTextInput())
    category = SelectField(choices = my_choices, default = '1')

    def validate_name(form, field):
        if len(field.data) > 50:
            raise ValidationError(_('Name must be less than 50 characters'))

    def validate_email(form, field):
        if len(field.data) > 60:
            raise ValidationError(_('Email must be less than 60 characters'))

    def validate_price(form, field):
        if len(field.data) > 8:
            raise ValidationError(_('Price must be less than 9 integers'))

    def validate_password(form, field):
        if not logged_in and not field:
            raise ValidationError(_('Password is required'))

Будет ли работать вышеуказанный validate_password для достижения желаемого эффекта? Есть ли другой лучший способ? Другой способ, которым я мог бы подумать, - это иметь 2 разных класса формы, и в посте http я создаю экземпляр класса формы, это должно быть:

def post(self):
    if not current_user:
      form = AdForm(self.request.params)
    if current_user:
      form = AdUserForm(self.request.params)

Мне также нужна условная проверка для поля категории, когда выбрана определенная категория, появляется больше вариантов, и они должны иметь проверку только для определенной базовой категории, например. пользователь выбирает «Автомобиль», а затем через Ajax может выбрать регистрационные данные и пробег для автомобиля, и эти поля обязательны для заполнения, если была выбрана категория «Автомобиль».

Так что это могут быть два вопроса, но оба случая касаются того, как я могу сделать поле "условно необязательным" или "условно обязательным".

Моя форма выглядит так

enter image description here

А для вошедшего в систему пользователя я предварительно заполняю имя и адрес электронной почты, и поле пароля просто не используется, поэтому поле пароля не подходит ни как «необязательный», ни «обязательный», ему потребуется что-то вроде «условно необязательный» или «условно требуется».

enter image description here

Спасибо за любой ответ или комментарий

Ответы [ 3 ]

64 голосов
/ 11 декабря 2011

Я не уверен, что это вполне соответствует вашим потребностям, но я использовал RequiredIf пользовательский валидатор для полей раньше, что делает поле обязательным, если другое поле имеет значение в форме ... например, в В сценарии «дата-время-часовой пояс» можно указать, чтобы в поле часового пояса было значение, если пользователь ввел дату и время.

class RequiredIf(Required):
    # a validator which makes a field required if
    # another field is set and has a truthy value

    def __init__(self, other_field_name, *args, **kwargs):
        self.other_field_name = other_field_name
        super(RequiredIf, self).__init__(*args, **kwargs)

    def __call__(self, form, field):
        other_field = form._fields.get(self.other_field_name)
        if other_field is None:
            raise Exception('no field named "%s" in form' % self.other_field_name)
        if bool(other_field.data):
            super(RequiredIf, self).__call__(form, field)

Конструктор берет имя другого поля, которое вызывает необходимость заполнения этого поля, например:

class DateTimeForm(Form):
    datetime = TextField()
    timezone = SelectField(choices=..., validators=[RequiredIf('datetime')])

Это может быть хорошей отправной точкой для реализации необходимой вам логики.

5 голосов
/ 20 августа 2014

Я нашел этот вопрос полезным и, основываясь на ответе @dcrosta, я создал еще один валидатор, который не является обязательным. Преимущество заключается в том, что вы можете комбинировать его с другими валидаторами wtforms. Вот мой необязательный валидатор, который проверяет другое поле. Поскольку мне нужно было проверить значение другого поля по отношению к некоторому определенному значению, я добавил пользовательскую проверку значения:

class OptionalIfFieldEqualTo(wtf.validators.Optional):
    # a validator which makes a field optional if
    # another field has a desired value

    def __init__(self, other_field_name, value, *args, **kwargs):
        self.other_field_name = other_field_name
        self.value = value
        super(OptionalIfFieldEqualTo, self).__init__(*args, **kwargs)

    def __call__(self, form, field):
        other_field = form._fields.get(self.other_field_name)
        if other_field is None:
            raise Exception('no field named "%s" in form' % self.other_field_name)
        if other_field.data == self.value:
            super(OptionalIfFieldEqualTo, self).__call__(form, field)
3 голосов
/ 18 мая 2017

Ответ от @dcrosta отличный, но я думаю, что некоторые вещи изменились в wtforms после этого ответа. Наследование от DataRequired добавляет атрибут required в поле формы, поэтому условный валидатор никогда не вызывается. Я внес небольшое изменение в класс из @dcrosta, который работает с wtforms 2.1. Это переопределяет field_flags, поэтому проверка браузера не выполняется.

from wtforms.validators import DataRequired


class RequiredIf(DataRequired):
    """Validator which makes a field required if another field is set and has a truthy value.

    Sources:
        - http://wtforms.simplecodes.com/docs/1.0.1/validators.html
        - /6791882/kak-sdelat-pole-uslovno-neobyazatelnym-v-wtforms

    """
    field_flags = ('requiredif',)

    def __init__(self, other_field_name, message=None, *args, **kwargs):
        self.other_field_name = other_field_name
        self.message = message

    def __call__(self, form, field):
        other_field = form[self.other_field_name]
        if other_field is None:
            raise Exception('no field named "%s" in form' % self.other_field_name)
        if bool(other_field.data):
            super(RequiredIf, self).__call__(form, field)

Более идеальное решение могло бы выполнить проверку в браузере, как текущее поведение DataRequired.

...