【问题标题】:Form does not validate. csrf_token included表单不验证。包含 csrf_token
【发布时间】:2020-04-22 09:26:04
【问题描述】:

我的 Flask 应用程序中的 validate_on_submit() 方法存在以下问题。

快速概览我所拥有的:

我的表单类是根据输入动态创建的:

class EditUsers(FlaskForm):

    submit = SubmitField('Submit')

def form_builder(roles_list):

    class EditUsersForm(EditUsers):
        pass

    for role in roles_list:
        if isinstance(role, tuple) and len(role) == 5:
            if not role[2] and role[3]:
                setattr(EditUsersForm, f'{role[0]}_{role[4]}_bool', BooleanField(label=role[0]))
                setattr(EditUsersForm, f'{role[0]}_{role[4]}_date', SelectField('Expiration Period', choices=choices))
        else:
            raise IncorrectType

IncorrectType 是我准备的自定义异常,并且选择是使用同一文件中的日期时间创建的(这无关紧要,因此我没有将其包含在代码中)。

我在烧瓶应用程序中的路线(简化):

#### EDIT: I pasted the wrong route, POST and GET Methods are included###
@edit_users.route('/users', methods=['GET', 'POST'])

def users():
...  # Some code there
form = form_builder(roles_to_form)

print(form.is_submitted())
print(form.validate())
print(form.validate_on_submit())

return render_template('edit_user.html',
                       data_dict=data_dict,  # in data_dict i pass form fields names and some other data
                       form=form,
                       )

我的模板:

<!-- Some usual stuff goes there css js etc -->

<div >
    <form class="form form-horizontal" method="post" role="form" style="margin: auto; text-align: center; width: 40%;">
        {{ form.csrf_token() }}
        <table class="table" align="center">
            <thead>
                <th>Role</th>
                <th>Expiration Date</th>
                <th>Give Role?</th>
            </thead>
            {% for field in data_dict['id_list'] %}
            <tr>
                <td align="left">{{ field[2] }}</td>
                <td align="left">
                    {{ form[field[1]] }}
                </td>
                <td align="left">{{ form[field[0]] }}</td>
            </tr>
            {% endfor %}
            <tr>
                <td colspan="3" align="center">{{ form.submit() }}</td>
            </tr>
        </table>
    </form>
</div>

我的问题是什么?

当我点击提交按钮时,is_submitted() 返回 True 但 validate() 不返回,因此我不能使用典型的 if form.validated_on_submit(),因为即使我提交表单也会返回 False。

我挖了一点,发现了一些不寻常的东西。我使用受保护的表单属性成员来检查 validate() 函数视为输入的任何内容:

for name in form._fields:
    print(name)
    inline = getattr(form.__class__, 'validate_%s' % name, None)
    print(inline)

输出(不要介意字段名称):

提交

Engineer_2_bool

Engineer_2_date

Operator_3_bool

Operator_3_date

Supervisor_4_bool

Supervisor_4_date

Guest_5_bool

Guest_5_date

csrf_token

我不知道为什么我的表单没有validate_{name} 属性。

【问题讨论】:

  • 这里没有足够的信息可以关闭。 choices 是字符串列表还是元组列表?在致电form.validate() 后,您是否检查过form.errors 的输出? (只需在print(form.validate()) 之后执行print(form.errors)
  • @tresni 我最近检查了错误,确实问题出在choices。我忘记将其类型从 datetime.datetime 更改为 str。感谢您的输入

标签: python-3.x flask jinja2 flask-wtforms wtforms


【解决方案1】:

我从你的代码中做了一个工作示例,我最终遇到了你描述的问题。

如果我很好地反转了您的代码,那么 form[field[1]] 似乎是您的 BooleanField 或 SelectField。所以要在模板中渲染它,你必须使用form[field[1]]()(也可以使用render_field)。 所以:

<tr>
    <td align="left">{{ field[2] }}</td>
    <td align="left">
        {{ form[field[1]] }}
    </td>
    <td align="left">{{ form[field[0]] }}</td>
</tr>

改正为:

<tr>
    <td align="left">{{ field[2] }}</td>
    <td align="left">
        {{ form[field[1]]() }}
    </td>
    <td align="left">{{ form[field[0]]() }}</td>
</tr>

https://flask.palletsprojects.com/en/1.1.x/patterns/wtforms/#forms-in-templates

当您使用 FlaskForm 时,您必须在模板中将 {{ form.csrf_token() }} 替换为 {{ form.csrf_token }} https://flask-wtf.readthedocs.io/en/stable/csrf.html#html-forms [编辑没有任何区别]

[根据 w8eight 的评论编辑] 该路由未获得 POST 授权(并且表单发出 POST 请求,如 &lt;form class="form form-horizontal" method="post" [...] 所示。 所以你必须改变:@edit_users.route('/users', methods=['GET'])@edit_users.route('/users', methods=['GET','POST'])

【讨论】:

  • 不幸的是,这不会改变表单的行为。按提交按钮后仍未验证
  • 刚刚对我的回答进行了更正。我错过了不允许使用 POST 方法
  • 很好,但不幸的是,这是我在创建问题时的错误,而不是在代码中。在我的代码中,这两种方法都传递给路由,我相应地编辑了我的问题
  • 非常感谢您的帮助,问题出在我的选择上,我忘记将类型从 datetime 更改为 str
【解决方案2】:

问题在于传递给SelectField 选项的数据类型。我将带有datetime.datetime 的元组列表传递给它。当类型更改为str 时,一切正常。

【讨论】:

    猜你喜欢
    • 2014-04-28
    • 2022-01-14
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 2015-12-02
    • 2015-08-14
    • 1970-01-01
    相关资源
    最近更新 更多