问题描述
|
我有一个UserForm类:
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()])
并希望在UpdateUserForm中将密码字段设为Optional:
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\',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\',widget=Passwordinput())
UserForm.__init__(self,**kwargs)
最后,我需要什么:
class UpdateUserForm(UserForm):
def __init__(self,**kwargs):
UserForm.__init__(self,formdata,obj,prefix,**kwargs)
self[\'password\'].validators = [validators.Optional()]
self[\'password\'].flags.required = False
解决方法
关于您在遍历表单对象时如何协调字段的第一个问题,这是我所做的:
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
。
,我通过在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)
,这是我如何完成您想做的事情:
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。这似乎为我工作。
,发生这种情况是因为字段排序是由UnboundField.creation_counter类定义的,该类使用Field类在代码中出现的顺序。
>>> x1 = UserForm()
>>> x2 = UpdateUserForm()
>>> [(f[0],f[1].creation_counter) for f in x1._unbound_fields]
[(\'first_name\',22),(\'last_name\',23),(\'middle_name\',24),(\'username\',25),(\'password\',26),(\'email\',27)]
>>> [(f[0],f[1].creation_counter) for f in x2._unbound_fields]
[(\'first_name\',27),28)]
>>>
由于这很难解决(因为wtforms尝试使用这种方法变得神奇),因此解决此问题的最佳方法是按所需顺序定义字段。
class BaseForm(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()])
class UserForm(BaseForm):
password = TextField(u\'Password\',[validators.Required()],widget=PasswordInput())
email = TextField(u\'Email\',[validators.Optional(),validators.Email()])
class UpdateUserForm(BaseForm):
password = TextField(u\'Password\',[validators.Optional()],validators.Email()])
但是,如果您是完美主义者或需要遵循DRY原则:
class BaseForm(Form):
first_name = TextField(u\'First name\',widget=PasswordInput())
class UpdateUserForm(BaseForm):
password = TextField(u\'Password\',widget=PasswordInput())
BaseForm.email = TextField(u\'Email\',validators.Email()])
,要强制对表单字段进行排序,可以使用以下方法:
from collections import OrderedDict
def order_fields(fields,order):
return OrderedDict((k,fields[k]) for k in order)
并在您的表单构造函数中调用它,如下所示:
class FancyForm(Form,ParentClass1,ParentClass2...):
x = TextField()
y = TextField()
z = TextField()
_order = \'x y z\'.split()
def __init__(self,**kwargs):
super(FancyForm,**kwargs)
self._fields = order_fields(self._fields,self._order + ParentClass1._order + ParentClass2._order)
,我已将以下两个片段合并为两个答案:
def __iter__(self):
ordered_fields = collections.OrderedDict()
for name in getattr(self,[]):
ordered_fields[name] = self._fields.pop(name)
ordered_fields.update(self._fields)
self._fields = ordered_fields
return super(BaseForm,self).__iter__()
在BaseForm上,我的每个表单都是其子级。基本上,在field_order中定义的所有内容均按该顺序进行,其余字段按原样呈现。