wtforms表单类子类和字段顺序

sec*_*119 15 python subclassing wtforms

我有一个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()])
Run Code Online (Sandbox Code Playgroud)

并希望在UpdateUserForm中使密码字段成为可选:

class UpdateUserForm(UserForm):
    password = TextField(u'Password', [validators.Optional()], widget=PasswordInput())
Run Code Online (Sandbox Code Playgroud)

但密码字段位于电子邮件字段之后,而不是之前.

如何在子类化时保留字段顺序?

此外,当我尝试更改密码字段验证器时它不起作用 - 密码仍然是必需的:/为什么?

class UpdateUserForm(UserForm):
    def __init__(self, **kwargs):
        self.password.validators = [validators.Optional()]
        super(UpdateUserForm, self).__init__(**kwargs)
Run Code Online (Sandbox Code Playgroud)

要么

class UpdateUserForm(UserForm):
    def __init__(self, **kwargs):
        self.password = TextField(u'Password', [validators.Optional()], widget=PasswordInput())
        super(UpdateUserForm, self).__init__(**kwargs)
Run Code Online (Sandbox Code Playgroud)

一些想法......

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)
Run Code Online (Sandbox Code Playgroud)

最后,我需要的是:

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
Run Code Online (Sandbox Code Playgroud)

rhy*_*yek 12

关于在迭代表单对象时重写字段的第一个问题,这就是我所做的:

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', '*')
Run Code Online (Sandbox Code Playgroud)

这样,当你渲染NewUserForm(可能是从一个模板,在形式的字段呈现场迭代),你会看到username,password,full_name.通常你会看到username最后一个.


Car*_*res 2

发生这种情况是因为字段排序是由 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', 22), ('last_name', 23), ('middle_name', 24), ('username', 25), ('email', 27), ('password', 28)]
>>> 
Run Code Online (Sandbox Code Playgroud)

由于这个问题很难解决(因为 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()], widget=PasswordInput())
    email = TextField(u'Email', [validators.Optional(), validators.Email()])
Run Code Online (Sandbox Code Playgroud)

但如果您是完美主义者或需要遵守DRY 原则

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

class UpdateUserForm(BaseForm):
    password = TextField(u'Password', [validators.Optional()], widget=PasswordInput())

BaseForm.email = TextField(u'Email', [validators.Optional(), validators.Email()])
Run Code Online (Sandbox Code Playgroud)