【问题标题】:Form isn't validating despite having a csrf token尽管有 csrf 令牌,但表单未验证
【发布时间】:2020-09-26 18:03:45
【问题描述】:

我在这里看到了一些类似的问题,但似乎没有一个解决方案在这里适用(问题通常是它缺少 csrf 令牌,这里不是这种情况)。

我有一个包含四个字段的表单- 3 个带有 SelectField 的下拉列表和一个 StringField- 使用烧瓶 wtforms 构建。我尝试向它添加一个编辑功能,它使用相同的 HTML 模板,但是它没有得到验证(没有进入 form.validate_on_submit 部分)。这是函数的代码:

@app.route('/movements/<int:movement_id>/edit', methods=['GET', 'POST'])   
def edit_movement(movement_id):
    movement = Movement.query.get_or_404(movement_id)
    form = MovementForm()
    if form.validate_on_submit():
        product = Product.query.filter_by(id=form.product.data).first()
        from_location = Location.query.filter_by(id=form.from_location.data).first()
        to_location = Location.query.filter_by(id=form.to_location.data).first()
        if int((Balance.query.filter_by(product = product.name).filter_by(location = from_location.name).first()).balance) < int(form.quantity.data) and from_location.name != "":
           flash("Invalid movement. Quantity of the product is insufficient.")
        else: 
            movement.product_id = product.id
            movement.product = product.name
            movement.from_location_id = from_location.id
            movement.from_location = from_location.name
            movement.to_location_id = to_location.id
            movement.to_location = to_location.name
            movement.quantity = form.quantity.data
            db.session.commit()
            flash('The product movement has been edited!', 'success')
        return redirect(url_for('movements'))
    elif request.method == 'GET':
        form.product.choices = [(product.id,product.name) for product in Product.query.all()]
        form.from_location.choices = [(location.id,location.name) for location in Location.query.all()]
        form.to_location.choices = [(location.id,location.name) for location in Location.query.all()]
        form.quantity.data = movement.quantity
    edit_button = True   
    return render_template('movements.html',form=form, edit_button=edit_button)

这是表单的代码:

class MovementForm(FlaskForm):
    product = SelectField("Product", choices = [])
    from_location = SelectField("From Location", choices = [], coerce=int)
    to_location = SelectField("To Location", choices = [], coerce=int)
    quantity = StringField("Quantity", validators=[DataRequired()])
    add_movement = SubmitField("Add Movement")

这是桌子的模型:

class Movement(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
    product = db.Column(db.String(50), nullable=False)
    from_location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
    from_location = db.Column(db.String(50))
    to_location_id = db.Column(db.Integer, db.ForeignKey('location.id'))
    to_location = db.Column(db.String(50))
    quantity = db.Column(db.Integer, nullable=False)
    timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

表单的 HTML 代码:

<form action="" method="POST">
        {{ form.csrf_token }}
        {{ form.product.label }}
        {{ form.product }}
        {{ form.from_location.label }}
        {{ form.from_location }}
        {{ form.to_location.label }}
        {{ form.to_location }}
        {{ form.quantity.label }}
        {{ form.quantity }}
        {% if edit_button %}
            <input type="submit" value="Edit Movement">
        {% else %}
            {{ form.add_movement }}
        {% endif %}
</form>

【问题讨论】:

    标签: python html flask flask-wtforms


    【解决方案1】:

    validate_on_submit 是一个方便的函数,它结合了对表单是否已提交的检查(即 POST、PUT、PATCH 或 DELETE)与对 form.validate 的调用。如果验证失败,form.errors 持有的字典将填充有用的信息。

    如果validate_on_submit 返回 False,则调试问题的一个有用步骤是记录(打印)form.errors 的内容。

    【讨论】:

    • {'product': ['Not a valid choice'], 'from_location': ['Not a valid choice'], 'to_location': ['Not a valid choice']}- 这是form.errors 产生的结果,所以很明显问题出在SubmitField 选项上。你会碰巧知道他们有什么问题吗?
    • wtforms.readthedocs.io/en/2.3.x/fields 说“注意,choices 关键字只计算一次,所以如果你想创建一个动态下拉列表,你需要在实例化后将选择列表分配给字段。”这建议删除choices = []
    【解决方案2】:

    你代码中的form.errors如下:

    {'product': ['Not a valid choice'], 'from_location': ['Not a valid choice'], 'to_location': ['Not a valid choice']}
    

    所以validate_on_submit返回False是合理的。

    如果您注释掉这些字段并只留下它应该有效的数量。以下更改对我有用,我设法更新了数据库中的数量。如果它也适用于您,那么您可以尝试取消注释每个字段并进一步调试。

    将表格更改为:

    class MovementForm(FlaskForm):
    quantity = StringField("Quantity", validators=[DataRequired()])
    add_movement = SubmitField("Add Movement")
    

    以及去往的路线:

    @app.route('/movements/<int:movement_id>/edit', methods=['GET', 'POST'])
    def edit_movement(movement_id):
    movement = Movement.query.get_or_404(movement_id)
    form = MovementForm()
    if form.validate_on_submit():
        movement.quantity = form.quantity.data
        db.session.commit()
        flash('The product movement has been edited!', 'success')
        return redirect(url_for('movements'))
    elif request.method == 'GET':
        form.quantity.data = movement.quantity
    print(form.errors)
    edit_button = True
    return render_template('movements.html', form=form, edit_button=edit_button)
    

    将模板更改为:

    <form action="" method="POST">
        {{ form.csrf_token }}
        {{ form.quantity.label }}
        {{ form.quantity }}
        {% if edit_button %}
            <input type="submit" value="Edit Movement">
        {% else %}
            {{ form.add_movement }}
        {% endif %}
    </form>
    

    更新:

    您面临的问题描述为here

    请尝试以下操作,如果它按预期工作,然后类似地设置其余字段(它对我有用,我设法更新了数据库中的数量和 from_location/id):

    形式:

    class MovementForm(FlaskForm):
    fromloc = [(location.id,location.name) for location in Location.query.all()]
    from_location_id = SelectField("From Location ID", choices = fromloc, coerce=int)
    quantity = StringField("Quantity", validators=[DataRequired()])
    add_movement = SubmitField("Add Movement")
    

    路线:

    @app.route('/movements/<int:movement_id>/edit', methods=['GET', 'POST'])
    def edit_movement(movement_id):
        movement = Movement.query.get_or_404(movement_id)
        form = MovementForm()
        if form.validate_on_submit():
            movement.from_location_id = form.from_location_id.data
            movement.from_location = (Location.query.filter_by(id = form.from_location_id.data).first()).name
            movement.quantity = form.quantity.data
            db.session.commit()
            flash('The product movement has been edited!', 'success')
            return redirect(url_for('movements'))
        elif request.method == 'GET':
            form.from_location_id.choices = [(location.id,location.name) for location in Location.query.all()]
            form.quantity.data = movement.quantity
        print(form.errors)
        edit_button = True
        return render_template('movements.html', form=form, edit_button=edit_button)
    

    模板:

    <form action="" method="POST">
        {{ form.csrf_token }}
        {{ form.from_location_id.label }}
        {{ form.from_location_id }}
        {{ form.quantity.label }}
        {{ form.quantity }}
        {% if edit_button %}
            <input type="submit" value="Edit Movement">
        {% else %}
            {{ form.add_movement }}
        {% endif %}
    </form>
    

    【讨论】:

    • 但我需要能够编辑所有字段。不过谢谢你,现在我知道问题出在编辑 SelectFields 上。
    【解决方案3】:

    我仍然不太清楚为什么我的表单没有验证,但是在我将 if form.validate_on_submit() 替换为 if request.method == 'POST' 后,我的函数开始工作了。

    【讨论】:

      猜你喜欢
      • 2020-08-07
      • 1970-01-01
      • 2014-01-06
      • 2019-03-22
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 2018-02-24
      • 1970-01-01
      相关资源
      最近更新 更多