如何使用带有附加参数的WTForms FormField?

时间:2017-12-11 06:33:46

标签: python flask wtforms flask-wtforms

我已经获得了带参数资源的表单LimitForm

class LimitForm(FlaskForm):
        def __init__(self, resource: Resource, *args, **kwargs):
           super(LimitForm, self).__init__(*args, **kwargs)
           self.period.choices = Period.choices()

我想通过FormField以其他形式使用此表单作为字段。像一个

class LimitsForm(FlaskForm)
   limits = FieldList(FormField(LimitForm))

但此代码引发异常 TypeError:__ init __()缺少1个必需的位置参数:' resource'

如何从LimitsForm传递资源到LimitForm?

由于

1 个答案:

答案 0 :(得分:0)

您可能会发现,在对封闭类进行常规初始化之后,执行一些附加的逻辑会比较容易,例如最后用特殊的逻辑实现自己的LimitsForm.__init__()。否则,您可能必须继承FieldListFormField的子类,并覆盖它们的process方法,如果您只需要为一个用例做一个简单的额外步骤,生活就会变得很复杂!

例如,我有一种情况,我想做与您要求的类似的事情,并通过FormField传递一些特殊的kwarg。 Flask-WTF默认情况下似乎不执行此操作。在这种情况下,我使用的QuerySelectField在调用SQLAlchemy时要传递一些特殊的过滤器。

在我的控制器中,对此的调用就像

form = CampaignForm(obj=campaign, studio=campaign.studio)

此处,工作室是特殊过滤器的一部分,仅在运行时才知道。但是,此studio kwarg不能通过嵌套的FormField来实现。

因为我必须扩展和重新连接Flask-WTF中的核心逻辑,才能使此kwarg通过FormField传递到GatingExtraForm中,所以我对此问题的解决方案是:

class GatingExtraForm(FlaskForm):
    class Meta:
        # we do not need CSRF in sub-forms
        csrf = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.set_query(studio=kwargs.get('studio'))

    # when this form is nested in a FormField, the studio kwarg may not be passed to __init__, 
    # therefore we make this method available for some post-initialization.
    def set_query(self, studio=None):
        self.gating_campaign.query = db.query(InAppCampaign).filter(InAppCampaign.active)

        if studio:
            self.gating_campaign.query = self.gating_campaign.query.filter(InAppCampaign.studio == studio)

        self.gating_campaign.query = self.gating_campaign.query.order_by(InAppCampaign.name)

    gating_campaign = QuerySelectField(
        "Gating Campaign",
        get_label='name',
        allow_blank=False,
        validators=[validators.Optional()]
    )
    # additional fields here....

class CampaignForm(FlaskForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # because there is no naive way to pass kwargs through on FormField init, 
        # just do some post init special processing
        self.gating_extra.form.set_query(studio=kwargs.get('studio'))

    # additional fields here....
    gating_extra = FormField(GatingExtraForm)

我承认,这有点骇人听闻,但有时对于少数用例来说,这是一个合理的权衡。如果事实证明这是一种常见模式,那么您可以更深入地扩展Flask-WTF。