Search code examples
formsflaskflask-wtformswtformsfieldlist

Flask-WTF not validating with FieldList subforms


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.


Solution

  • 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.