尽管有 csrf 令牌,表单仍未验证
Form isn't validating despite having a csrf token
我在这里看到了几个类似的问题,但是 none 的解决方案似乎适用于这里(问题通常是它缺少 csrf 令牌,这里不是这种情况)。
我有一个包含四个字段的表单 - 3 个带有 SelectField 的下拉列表和一个 StringField - 使用 flask 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")
这是 table 的模型:
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>
validate_on_submit
是一个方便的函数,它将检查表单是否已提交(即 POST、PUT、PATCH 或 DELETE)与调用 form.validate
相结合。如果验证失败,form.errors
持有的字典将填充有用的信息。
调试问题的一个有用步骤是记录(打印)form.errors
if validate_on_submit
returns False.
的内容
你代码中的form.errors
如下:
{'product': ['Not a valid choice'], 'from_location': ['Not a valid choice'], 'to_location': ['Not a valid choice']}
所以validate_on_submit
到returnFalse
也是合理的。
如果您注释掉这些字段并只保留它应该起作用的数量。以下更改对我有用,我设法更新了数据库中的数量。如果它也适用于您,那么您可以尝试取消注释每个字段并进一步调试。
将表格更改为:
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>
我仍然不太清楚为什么我的表单没有通过验证,但是在我将 if form.validate_on_submit()
替换为 if request.method == 'POST'
后我的函数开始工作了。
我在这里看到了几个类似的问题,但是 none 的解决方案似乎适用于这里(问题通常是它缺少 csrf 令牌,这里不是这种情况)。
我有一个包含四个字段的表单 - 3 个带有 SelectField 的下拉列表和一个 StringField - 使用 flask 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")
这是 table 的模型:
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>
validate_on_submit
是一个方便的函数,它将检查表单是否已提交(即 POST、PUT、PATCH 或 DELETE)与调用 form.validate
相结合。如果验证失败,form.errors
持有的字典将填充有用的信息。
调试问题的一个有用步骤是记录(打印)form.errors
if validate_on_submit
returns False.
你代码中的form.errors
如下:
{'product': ['Not a valid choice'], 'from_location': ['Not a valid choice'], 'to_location': ['Not a valid choice']}
所以validate_on_submit
到returnFalse
也是合理的。
如果您注释掉这些字段并只保留它应该起作用的数量。以下更改对我有用,我设法更新了数据库中的数量。如果它也适用于您,那么您可以尝试取消注释每个字段并进一步调试。
将表格更改为:
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>
我仍然不太清楚为什么我的表单没有通过验证,但是在我将 if form.validate_on_submit()
替换为 if request.method == 'POST'
后我的函数开始工作了。