首页 > 解决方案 > Flask WTForms 动态选择字段验证值在 POST 上为空

问题描述

我无法验证动态生成的 WTForms SelectField。不知何故,我self.choices在 POST 期间是空的,而它们在第一个 GET 请求中填充。我错过了什么吗?

应用程序.py:

    @app.route('/users/edit/<email>/', methods=['GET', 'POST'])
    def users_edit(email):

        match = False
        for user in session['users']['message']:
            if user['email']['S'] == email:
                match = True

        if match:
            form = EditUserForm()
            if request.method == 'GET':
                form_data = session.get('edit_user_form_data', None)
                if form_data:
                    form = EditUserForm(MultiDict(form_data))
                    form.shops_allowed.choices = [(str(shop['shop_id']['S']), str(shop['general']['M']['shop_name']['S'])) for i, shop in enumerate(session['shops']['message'])]

                    print(f"SHOPS_ALLOWED CHOICES WITH FORM DATA: {form.shops_allowed.choices}")

                    form.validate()

                    session.pop('edit_user_form_data', None)
                else:
                    form.email.data = email
                    form.shops_allowed.choices = [(str(shop['shop_id']['S']), str(shop['general']['M']['shop_name']['S'])) for i, shop in enumerate(session['shops']['message'])]

                    print(f"SHOPS_ALLOWED CHOICES WITHOUT FORM DATA: {form.shops_allowed.choices}")
                    matched_shop = []
                    for user in session['users']['message']:
                        if user['email']['S'] == email and 'shops_allowed' in user.keys():
                            matched_shop = [allow['S'] for allow, shop in zip(user['shops_allowed']['L'], session['shops']['message'])]

                    form.shops_allowed.data = matched_shop
                    form.validate()

            if request.method == 'POST' and form.validate_on_submit():
                app.logger.info(f"PRE POST EDIT USER FORM DATA: {form.data}")

                url = ...

                <REDACTED POST REQUESTS ENDPOINT>

                app.logger.info(f"POST EDIT USER RESPONSE: ({post_response.status_code}) {response_data}")

                if post_response.status_code == 200:
                    session.pop('edit_user_form_data', None)
                    return redirect(url_for('users'))
                else:
                    app.logger.info(f"EDIT USER FORM DATA: {form.data}")
                    session['edit_user_form_data'] = form.data
                    return redirect(url_for('users_edit', email=email))
            elif request.method == 'POST' and form.validate_on_submit() == False:
                app.logger.info(f"EDIT USER FORM DATA VALIDATION FAILED: {form.data}")
                session['edit_user_form_data'] = form.data
                return redirect(url_for('users_edit', email=email))

表格.py

class NonValidatingSelectMultipleField(SelectMultipleField):
    def __len__(self):
        return 1

    def pre_validate(self, form):
        pass
        # HACK TO VALID A CHOICE FOR SELECT MULTIPLE
        if self.data:
            print(f"SELF DATA: {self.data}")
            print(f"VALUES: {self.choices}")
            values = list(self.coerce(c[0]) for c in self.choices)
            print(f"PROCESSED VALUES: {values}")
            for d in self.data:
                if d not in values:
                    raise ValueError(self.gettext("'%(value)s' is not a valid choice for this multiple select field") % dict(value=d))


class EditUserForm(FlaskForm):
    email = StringField('Email', [
        validators.DataRequired(),
        validators.Length(min=6),
        validators.Email(message='Not a valid email address.')]
    )
    shops_allowed = NonValidatingSelectMultipleField(
        'Shops',
        widget = widgets.ListWidget(prefix_label=False),
        option_widget = widgets.CheckboxInput(),
        coerce=str
    )

这将呈现以下日志记录:

SHOPS_ALLOWED CHOICES WITHOUT FORM DATA: [('decb5a00-fe45-484f-b887-b9bd41c4f0f2', 'My shop'), ('2cea2efa-7ccf-4bca-896d-16119d5fb7a8', 'My other shop')]
SELF DATA: ['decb5a00-fe45-484f-b887-b9bd41c4f0f2', '2cea2efa-7ccf-4bca-896d-16119d5fb7a8']
VALUES: [('decb5a00-fe45-484f-b887-b9bd41c4f0f2', 'My shop'), ('2cea2efa-7ccf-4bca-896d-16119d5fb7a8', 'My other shop')]
PROCESSED VALUES: ['decb5a00-fe45-484f-b887-b9bd41c4f0f2', '2cea2efa-7ccf-4bca-896d-16119d5fb7a8']
127.0.0.1 - - [03/Apr/2020 14:30:11] "GET /users/edit/my@email.com/ HTTP/1.1" 200 -

发布后:

SELF DATA: ['decb5a00-fe45-484f-b887-b9bd41c4f0f2', '2cea2efa-7ccf-4bca-896d-16119d5fb7a8']
VALUES: None

在屏幕上:

forms.py", line 35, in pre_validate values = list(self.coerce(c[0]) for c in self.choices) TypeError: 'NoneType' object is not iterable

标签: pythonflaskdynamicflask-wtformsnonetype

解决方案


嗯,在再次查看我的代码后,我终于找到了问题所在。我打电话form.validate_on_submit()的同时我正在验证if request.method == 'POST'这永远不会让我有可能在 POST 期间填充选择。

将我的代码更改为:

            if request.method == 'POST':

                form.shops_allowed.choices = [(str(shop['shop_id']['S']), str(shop['general']['M']['shop_name']['S'])) for i, shop in enumerate(session['shops']['message'])]
                form.validate_on_submit()

它开始起作用了。有时你只需要写下来就能看到答案:-)


推荐阅读