I'm having trouble passing through validation when using a FieldList
with WTForms. I keep getting this error. {'csrf_token': [u'CSRF token missing']}
. The problem is if I do not have any data to validate in the FieldList
field, the validation passes and there are no issues. But when I try to validate the form with any data I get that error.
Here are my forms:
class FilterForm(wtf.Form):
filter_value = wtf.TextField('Value', validators=[validators.Required()])
filter_operator = wtf.SelectField('Operator', validators=[validators.Required()])
filter_compare_value=wtf.TextField('Compare Value', validators=[validators.Required()])
class RedirectForm(wtf.Form):
redirect_id = wtf.HiddenField('id')
redirect_name = wtf.TextField('Name', validators=[validators.Required()])
redirect_url = wtf.TextField('URL', validators=[validators.Required()])
redirect_type = wtf.SelectField('Type', validators=[validators.Required()])
redirect_method = wtf.SelectField('Method', validators=[validators.Required()])
redirect_active = wtf.BooleanField('Is Active')
redirect_filters_any = wtf.FieldList(wtf.FormField(FilterForm))
redirect_filters_all = wtf.FieldList(wtf.FormField(FilterForm))
The form seems to display correctly and works fine until I add data to either redirect_filters_any
or redirect_filters_all
Is there a way to disable csrf for the FieldList
or pass a CSRF value to the FieldList
? I want to keep CSRF protection enabled but can not seem to get past this validation problem.
Here is the Jinja2 template
{% extends "base.html" %}
{% set active_page = "endpoints" %}
{% block tail_script %}
<script src="/static/js/page/redirects.js"></script>
{% endblock %}
{% block content %}
<div class="row12">
<div class="span12">
<ul class="breadcrumb">
<li><a href="{{ url_for('list_endpoints') }}">Endpoints</a> <span class="divider">/</span></li>
<li><a href="{{ url_for('show_endpoint', id=endpoint_id) }}">{{endpoint_name}}</a> <span class="divider">/</span></li>
{% if redirect_id != 'new' %}
<li class="active">{{ form.redirect_name.data }}</li>
{% else %}
<li class="active">New</li>
{% endif %}
</ul>
<form action="{{ url_for('edit_redirect', endpoint_id=endpoint_id, redirect_id=redirect_id) }}" class="form-horizontal" method="post">
<legend>General</legend>
{{ form.hidden_tag() }}
<div class="control-group {% if form.redirect_name.errors %}error{% endif %}">
<div class="control-label">{{ form.redirect_name.label }}</div>
<div class="controls">
{{ form.redirect_name|safe }}
{% if form.redirect_name.errors %}
<span class="help-inline">
<ul class="errors">
{% for error in form.redirect_name.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
{% endif %}
</div>
</div>
<div class="control-group {% if form.redirect_type.errors %}error{% endif %}">
<div class="control-label">{{ form.redirect_type.label }}</div>
<div class="controls">
{{ form.redirect_type|safe }}
{% if form.redirect_type.errors %}
<span class="help-inline">
<ul class="errors">
{% for error in form.redirect_type.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
{% endif %}
</div>
</div>
<div class="control-group {% if form.redirect_active.errors %}error{% endif %}">
<div class="control-label">{{ form.redirect_active.label }}</div>
<div class="controls">
{{ form.redirect_active|safe }}
{% if form.redirect_active.errors %}
<span class="help-inline">
<ul class="errors">
{% for error in form.redirect_active.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
{% endif %}
</div>
</div>
<div class="control-group {% if form.redirect_method.errors %}error{% endif %}">
<div class="control-label">{{ form.redirect_method.label }}</div>
<div class="controls">
{{ form.redirect_method|safe }}
{% if form.redirect_method.errors %}
<span class="help-inline">
<ul class="errors">
{% for error in form.redirect_method.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
{% endif %}
</div>
</div>
<div class="control-group {% if form.redirect_url.errors %}error{% endif %}">
<div class="control-label">{{ form.redirect_url.label }}</div>
<div class="controls">
{{ form.redirect_url|safe }}
{% if form.redirect_url.errors %}
<span class="help-inline">
<ul class="errors">
{% for error in form.redirect_url.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
{% endif %}
</div>
</div>
<legend>Meet All Filters <a href="#" class="btn addAllFilter">Add</a></legend>
<table class="stable-striped" id="all_filter_table">
<tbody>
{% for f in form.redirect_filters_all %}
<tr style="vertical-align:top;">
<td>
{{ f.filter_value }}
{% if f.filter_value.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_value.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td>
{{ f.filter_operator }}
{% if f.filter_operator.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_operator.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td>
{{ f.filter_compare_value }}
{% if f.filter_compare_value.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_compare_value.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td><a href="#" class="btn remove">Remove</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<legend>Meet Any Filters <a href="#" class="btn addAnyFilter">Add</a></legend>
<table class="stable-striped" id="any_filter_table">
<tbody>
{% for f in form.redirect_filters_any %}
<tr style="vertical-align:top;">
<td>
{{ f.filter_value }}
{% if f.filter_value.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_value.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td>
{{ f.filter_operator }}
{% if f.filter_operator.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_operator.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td>
{{ f.filter_compare_value }}
{% if f.filter_compare_value.errors %}
<br>
<div class="control-group error">
<span class="help-inline">
<ul class="errors">
{% for error in f.filter_compare_value.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</span>
</div>
{% endif %}
</td>
<td><a href="#" class="btn remove">Remove</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% if g.user.user_type == 'admin' %}
<div class="control-group">
<div class="controls">
<input class="btn btn-primary" type="submit" value="Save"/>
<a href="{{url_for('show_endpoint', id=endpoint_id)}}" class="btn">Cancel</a>
</div>
</div>
{% endif %}
</form>
</div>
</div>
{% endblock %}
After encountering the same problem, I wanted to to supply a third option to the solution above
You can also override the constructor in your form class to replace the default value of csrf_enabled. This has the advantage that you can use the the same form definition as both a fieldlist member, and a standalone form with CSRF enabled by passing csrf_enabled=True.
Since version 1.0 the new way to achieve this is as follows: This will disable the CSRF token for all instances of your Form, so be careful to only use it as a subform.
It seems
csrf_enabled
is deprecated. Here's a solution that works withFlask-WTForms 0.14.2
, partially based on leebriggs's answer. Rather than pass a parameter when creating the form, I just created axNoCsrf
subclass, because I didn't want someone to accidentally forget to include the CSRF token when they do want it. This way, you have to typeNoCsrf
to get the non-CSRF version.Here is the documentation for
csrf
field of themeta
class.The issue seems to be that Flask-WTForms
Form
is actually a subclass ofwtforms.ext.SecureForm
- and the only way to disable the csrf protection on a form is to pass the keyword argumentcsrf_enabled=False
to the form when constructing it. SinceFormField
actually handles instantiating the form and you can either:FormField
that will let you pass in form keyword argumentsor
wtforms.Form
rather thanflask.ext.wtforms.Form
for yourFilterForm
(as long as you never display aFilterForm
on its own you won't need to worry about CSRF).