【问题标题】:How to make a field conditionally optional in WTForms?如何在 WTForms 中使字段有条件地可选?
【发布时间】:2012-01-17 18:59:34
【问题描述】:

我的表单验证工作已接近完成,我只有 2 种情况,我不知道如何解决:1)密码字段当然应该是必需的,但我也提供了使用 google 或 facebook 帐户登录的可能性通过 OAuth,然后预先填写名称,但我从表单中完全删除密码字段是否存在用户(谷歌)或 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)

I also need conditional validation for the category field, when a certain category is selected then more choices appear and these should have validation only for a certain base-category eg.用户选择“汽车”,然后通过 Ajax 可以选择汽车的注册数据和里程数,鉴于选择了汽车类别,这些字段是必需的。

所以这可能是两个问题,但两种情况都与我如何使字段“有条件可选”或“有条件要求”有关。

我的表单是这样的

对于登录用户,我预先填写了姓名和电子邮件地址,而密码字段根本没有使用,因此密码字段既不适合“可选”也不适合“必需”,它需要类似“有条件可选”或“有条件地要求。”

感谢您的任何回答或评论

【问题讨论】:

    标签: python google-app-engine validation wtforms


    【解决方案1】:

    我不确定这是否完全符合您的需求,但我之前在字段上使用过 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')])
    

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

    【讨论】:

    • 太棒了³!效果很好!
    • 知道如果包含条件的字段在当前表单的父表单中该怎么办吗?我在父表单的 FormField 中有子表单的字段,这些字段相对于父表单中的字段是有条件的。
    【解决方案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 属性,因此条件验证器永远不会被调用。我对与 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 的当前行为。

      【讨论】:

      • 这个方法好像在最近的一次更新中坏掉了。我所有的条件字段都是必需的。我不知道如何正确调试它。有什么建议吗?
      • 这可行,但不是使该字段成为必填项,而是导致内部服务器错误。有什么解决方法吗?
      猜你喜欢
      • 2014-12-30
      • 2012-10-07
      • 2015-01-20
      • 1970-01-01
      • 2018-10-25
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 2018-09-26
      相关资源
      最近更新 更多