I am trying to get a test form that includes subforms to work but the form does not validate on submission. The form itself is submitting a recipe with ingredients as its subforms using FieldList()
. I have also made sure to include hidden_tag()
in the HTML.
Forms:
class IngredientsForm(FlaskForm):
ingredient_name = StringField("Ingredient", validators=[DataRequired()])
class RecipeForm(FlaskForm):
recipe_name = StringField("Recipe name", validators=[DataRequired()])
ingredients = FieldList(FormField(IngredientsForm), min_entries=2, max_entries=5)
submit = SubmitField("Submit")
Views:
@app.route("/", methods=["GET", "POST"])
def index():
form = RecipeForm()
return render_template("index.html", form=form)
@app.route("/submit", methods=["POST"])
def submit():
form = RecipeForm()
print(f"ERRORS: {form.errors}")
print(f"DATA: {form.data}")
if form.validate_on_submit():
print("Validated!")
print(form.recipe_name)
for ingredient in form.ingredients.data:
print(ingredient)
return redirect("/")
else:
print("Form not validated")
return render_template("index.html", form=form)
HTML:
<h1>Enter recipe:</h1>
<form action="/submit" method="POST">
{{ form.hidden_tag() }}
<p>
{{ form.recipe_name.label }} <br>
{{ form.recipe_name() }}
</p>
<p>
{{ form.ingredients.label }} <br>
{% for ing in form.ingredients %}
{{ ing.ingredient_name.label }}
{{ ing.ingredient_name() }}
<br>
{% endfor %}
</p>
<p>
{{ form.submit() }}
</p>
</form>
Output:
ERRORS: {}
DATA: {'recipe_name': 'butterbread', 'ingredients': [{'ingredient_name': 'butter', 'csrf_token': None}, {'ingredient_name': 'bread', 'csrf_token': None}], 'submit': True, 'csrf_token': 'Ijg1NmVjZjIwODY3MTJkNDNkMTFiNDQ2YzdiNzYyYzYyNmUzNGUzMWMi.YtaF7g.WRn25PWYMFplr_KV7RoZq1uLgrI'}
Form not validated
127.0.0.1 - - [19/Jul/2022 03:22:44] "POST /submit HTTP/1.1" 200 -
So far, no errors show up but it looks like in the data that since each subform has None
as its csrf_token, maybe that's messing up the validation? I've tried getting this to validate for a while but to no avail.
CodePudding user response:
Figured it out. The problem is that the subform IngredientsForm inherits from FlaskForm which is a subclass that's csrf secure, and was the reason preventing validation. You don't need this as you have already the token in the main form.
Instead, have it inherit from wtforms.Form which doesn't have that. Another way is to disable csrf during init, but the previous method is preferred.
CodePudding user response:
You can disable csrf protection for the FlaskForm
by setting the flag to false within the class Meta
. CSRF protection is not necessary for nested forms as long as the parent form takes over this task.
class IngredientsForm(FlaskForm):
class Meta:
csrf = False
ingredient_name = StringField("Ingredient", validators=[DataRequired()])
class RecipeForm(FlaskForm):
recipe_name = StringField("Recipe name", validators=[DataRequired()])
ingredients = FieldList(FormField(IngredientsForm), min_entries=2, max_entries=5)
submit = SubmitField("Submit")
An alternative is to inherit from Form
.