wtforms表单类的子类化与字段排序
我有一个用户表单类:
class UserForm(Form):
first_name = TextField(u'First name', [validators.Required()])
last_name = TextField(u'Last name', [validators.Required()])
middle_name = TextField(u'Middle name', [validators.Required()])
username = TextField(u'Username', [validators.Required()])
password = TextField(u'Password', [validators.Required()], widget=PasswordInput())
email = TextField(u'Email', [validators.Optional(), validators.Email()])
我想在更新用户表单时,让密码字段变成可选的:
class UpdateUserForm(UserForm):
password = TextField(u'Password', [validators.Optional()], widget=PasswordInput())
但是密码字段放在电子邮件字段后面,而不是前面。
我该如何在子类中保持字段的顺序呢?
另外,当我尝试更改密码字段的验证规则时,它没有生效——密码仍然是必填的 :/ 这是为什么呢?
class UpdateUserForm(UserForm):
def __init__(self, **kwargs):
self.password.validators = [validators.Optional()]
super(UpdateUserForm, self).__init__(**kwargs)
或者
class UpdateUserForm(UserForm):
def __init__(self, **kwargs):
self.password = TextField(u'Password', [validators.Optional()], widget=PasswordInput())
super(UpdateUserForm, self).__init__(**kwargs)
一些想法...
class UpdateUserForm(UserForm):
def __init__(self, formdata=None, obj=None, prefix='', **kwargs):
self._unbound_fields[4][1] = TextField(u'Password', [validators.Optional()], widget=PasswordInput())
UserForm.__init__(self, formdata=None, obj=None, prefix='', **kwargs)
最后,我需要的是:
class UpdateUserForm(UserForm):
def __init__(self, formdata=None, obj=None, prefix='', **kwargs):
UserForm.__init__(self, formdata, obj, prefix, **kwargs)
self['password'].validators = [validators.Optional()]
self['password'].flags.required = False
6 个回答
3
这是我完成你想做的事情的方法:
class UserForm(wtforms.Form):
def __init__(self, *args, **kwargs):
super(UserForm,self).__init__(*args, **kwargs)
if kwargs.get('update', None):
self['passwd'].validators.append(wtforms.validators.Optional())
self['passwd'].flags.required = False
else:
self['passwd'].validators.append(wtforms.validators.Required())
passwd = UnicodeField(
u'Password',
[
wtforms.validators.length(max=50),
wtforms.validators.EqualTo(
'confirm',
message='Passwords must match'
)
],
widget = wtforms.widgets.PasswordInput()
)
confirm = wtforms.PasswordField(u'Password Verify')
然后,当我创建用户表单的时候,如果是编辑,我会传递一个参数update=True。这对我来说是有效的。
4
我通过在我的 Form
类中定义一个额外的 __order
属性来解决这个问题,并重写了 __iter__
方法,这样返回的迭代器数据就会按照定义的顺序进行排序。虽然这样做可能不是特别高效,但表单上的字段数量通常不多,所以不会造成太大问题。而且,这种方法也适用于从子类表单中继承的字段。
class MyForm(Form):
field3 = TextField()
field1 = TextField()
field2 = TextField()
__order = ('field1', 'field2', 'field3')
def __iter__(self):
fields = list(super(MyForm, self).__iter__())
get_field = lambda field_id: next((fld for fld in fields
if fld.id == field_id))
return (get_field(field_id) for field_id in self.__order)
13
关于你第一个问题,关于在遍历表单对象时如何重新排列字段,我是这样做的:
class BaseForm(Form):
def __iter__(self):
field_order = getattr(self, 'field_order', None)
if field_order:
temp_fields = []
for name in field_order:
if name == '*':
temp_fields.extend([f for f in self._unbound_fields if f[0] not in field_order])
else:
temp_fields.append([f for f in self._unbound_fields if f[0] == name][0])
self._unbound_fields = temp_fields
return super(BaseForm, self).__iter__()
class BaseUserForm(BaseForm):
password = PasswordField('Password', [Required()])
full_name = TextField('Full name', [Required()])
class NewUserForm(BaseUserForm):
username = Textfield('Username', [Required()])
field_order = ('username', '*')
这样,当你渲染 NewUserForm(可能是从一个模板中逐个字段地渲染表单)时,你会看到 username
、password
和 full_name
。通常情况下,你会看到 username
是最后一个。