Как сделать поле условно опциональным в WTForms?
Моя форма проверки работает почти полностью, у меня всего 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 post я определяю класс формы, который он должен:
def post(self):
if not current_user:
form = AdForm(self.request.params)
if current_user:
form = AdUserForm(self.request.params)
Мне также нужна условная проверка для поля категории, когда выбрана определенная категория, тогда появляется больше вариантов, и они должны иметь валидацию только для определенной базовой категории, например. пользователь выбирает "Автомобиль", а затем через Ajax может выбрать регистрационные данные и пробег для автомобиля, и эти поля необходимы, если выбрана категория Car.
Таким образом, это могут быть два вопроса, но оба случая связаны с тем, как я могу сделать поле "условно необязательным" или "условно обязательным".
Моя форма выглядит как
![enter image description here]()
И для зарегистрированного пользователя я предварительно заполняю имя и адрес электронной почты, и поле pasword просто не используется, поэтому поле пароля не подходит для "необязательного" или "обязательного", для этого потребуется что-то вроде "условно необязательно" или "условно требуется".
![enter image description here]()
Спасибо за любой ответ или комментарий
Ответы
Ответ 1
Я не уверен, что это вполне соответствует вашим потребностям, но я использовал настраиваемый валидатор RequiredIf
для полей ранее, что делает поле обязательным, если другое поле имеет значение в форме... например, в сценарий datetime и timezone, я могу сделать поле часового пояса обязательным для того, чтобы иметь значение, если пользователь ввел дату и время.
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')])
Это может быть хорошей отправной точкой для реализации нужной вам логики.
Ответ 2
Я нашел этот вопрос полезным и основываясь на ответе @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
Ответ от @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
- http://stackoverflow.com/info/8463209/how-to-make-a-field-conditionally-optional-in-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
.