如何在WTForms中使字段有条件地成为可选字段?

2024-05-19 21:13:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我的表单验证几乎完成了,我只知道两个我不知道如何解决的情况:1)当然需要密码字段,但我也提供了通过OAuth登录google或facebook帐户的可能性,然后name被预先填充,但如果有用户,我会从表单中完全删除密码字段(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>

所以对于那些已经登录并且密码字段没有意义的用户,我需要一些逻辑使该字段有条件地成为可选字段。我想我可以在我的表单类中有一个用于登录的变量+一个方法,例如:

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'))

上述验证密码是否能达到预期效果?还有更好的办法吗?我可以想到的另一种方法是有两个不同的表单类,在http post中,我实例化了表单类,它应该是:

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

我还需要对category字段进行条件验证,选择某个类别后,会出现更多的选项,这些选项应仅对某个基本类别进行验证,例如,用户选择“汽车”,然后通过Ajax可以选择汽车的注册数据和里程数,如果选择了汽车类别,则这些字段是必需的。

所以这可能是两个问题,但这两个问题都与我如何使字段成为“有条件可选”或“有条件必需”有关。

我的表格是这样的

enter image description here

而对于一个登录的用户,我在名字和电子邮件地址前加上前缀,而pasword字段根本不使用,因此password字段既不适合“可选”也不适合“必需”,它需要类似“有条件可选”或“有条件必需”的内容

enter image description here

谢谢你的回答或评论


Tags: 用户form密码表单fieldmessageifis
3条回答

来自@dcrosta的答案很好,但是我认为自从这个答案之后,wtforms中的一些事情已经发生了变化。从DataRequired继承会将required属性添加到表单字段,因此不会调用条件验证器。我对使用wtforms 2.1的@dcrosta类做了一个小更改。这只会超过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/questions/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的当前行为一样。

我发现这个问题很有用,并且基于@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)

我不确定这是否完全符合您的需要,但我以前在字段上使用过RequiredIf自定义验证器,这使得如果另一个字段具有表单中的值,则需要一个字段。。。例如,在datetime和timezone场景中,如果用户输入了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')])

这可能是实现所需逻辑的良好起点。

相关问题 更多 >