Flask - WTForm - save form to db

10,209

Solution 1

You need to put anti-forgery token somewhere within the form. Add the following in your template file, inside the form tag:

{{ form.hidden_tag() }}

This renders hidden field similar to

<input id="csrf_token" name="csrf_token" type="hidden" value="xxxxxx">

Putting it together with your form.html file we are getting:

{% extends "layout2.html" %}
{% from 'common.html' import edit_field %}
{% block content %}
<div class='edit well offset 2 span8'>
    <form method='post' class="form-horizontal">
        {{ form.hidden_tag() }}
        <legend>Add task</legend>
        {{ edit_field(form.name , class="span3") }}
        {{ edit_field(form.due_date , class="span3", type="datetime") }}
        {{ edit_field(form.priority, class="span3") }}
        {{ edit_field(form.description, rows="5" ,class="span3"
                     , placeholder="foobar" ) }}
        {{ edit_field(form.posted_date, class="span3",type="datetime") }}
        {{ edit_field(form.category, class="span3") }}
        {{ edit_field(form.super_category, class="span3") }}
        <div class="form-actions">
            <button type="submit" class="btn">SAVE</button>
        </div>
    </form>
</div>
{% endblock %}

Solution 2

The solution is :

add hidden_tag() - Thanks vittore !

import "form" from flask_wtf not from wtforms .

remove "request.form" from new_task()

Now code looks like this and it's working ! - thanks everyone for help .

@app.route('/add/',methods=['GET','POST'])
@login_required
def new_task():
    form = AddTask(csrf_enabled=True)
    if form.validate_on_submit():
        new_task = Tasks(
                          form.name.data,
                          form.due_date.data,
                          form.priority.data,
                          form.posted_date.data,
                          session['user_id'],
                          form.category.data,
                          form.super_category.data,
                          form.description.data
                        )
        db.session.add(new_task)
        db.session.commit()
        return redirect(url_for('tasks'))
    else:
        flash_errors(form)
    return render_template('form.html',form=form)

Solution 3

It looks like your form.validate() is returning False, try testing without validate check and see what happens, also im not seeing csrf token being passed anywhere.

Share:
10,209

Related videos on Youtube

Grunt
Author by

Grunt

amateur python coder

Updated on June 17, 2022

Comments

  • Grunt
    Grunt almost 2 years

    I have problem with saving form (wtf) to (sqlalchemy) db ,form is rendering ,but after submit nothing happens ...

    e.g. :

    127.0.0.1 - - [30/Dec/2013 10:30:24] "POST /add/ HTTP/1.1" 200 -
    

    I was trying to save without validators

    e.g. :

    name = TextField('Task Name')
    

    and i was trying to save form other way :

    if request.method == 'POST' and form.validate():
                new_task = Tasks(
                             form.name.data,
                             form.due_date.data,
                             form.priority.data,
                             form.posted_date.data,
                             session['user_id'],
                             form.category.data,
                             form.super_category.data,
                             form.description.data
                            )
                db.session.add(new_task)
                db.session.commit()  
    

    And code below :

    models :

    # -*- coding: utf-8; -*-
    
    from app import db
    import datetime
    
    
    class Tasks(db.Model):
    
        __tablename__ = "tasks"
    
        task_id = db.Column(db.Integer,primary_key=True)
        name = db.Column(db.String(255),nullable=False)
        description = db.Column(db.Text,nullable=False)
        priority = db.Column(db.Integer,nullable=False)
        posted_date = db.Column(db.DATE,nullable=False)
        status = db.Column(db.Integer,default=1,nullable=False)
        category = db.Column(db.String(255),nullable=False)
        super_category = db.Column(db.String(255),nullable=False)
        user_id = db.Column(db.Integer,db.ForeignKey('users.id'))
    
        def __init__(self,name,due_date,priority,posted_date,users_id,category,super_category,description):
            self.name = name
            self.due_date = due_date
            self.priority = priority
            self.posted_date = posted_date
            self.user_id = users_id
            self.category = category
            self.super_category = super_category
            self.description = description
    

    forms :

     # -*- coding: utf-8; -*-
    from wtforms import Form,validators
    from wtforms import TextField,DateField,IntegerField,SelectField,TextAreaField
    from wtforms.validators import required,Email,EqualTo,Length
    from wtforms import PasswordField
    
    class AddTask(Form):
      name = TextField('Task Name',validators=[required()])
      due_date = DateField('Date Due (mm/dd/yyyy)',validators=[required()],format='%m/%d/%Y')
      priority = SelectField('Priority',validators=[required()],choices=[('1','1'),('2','2'),('3','3'),('4','4'),('5','5')])
      description = TextAreaField('Description',validators=[required()])
      posted_date = DateField('Posted Date (mm/dd/yyyy)',validators=[required()],format='%m/%d/%Y')
      category = SelectField('Category',validators=[required()],choices=[('foobar','foobar'),('foobar2','foobar2')])
      super_category = SelectField('Super_category',validators=[required()],choices=[('foobar3','foobar3'),('foobar4','foobar4'),('foobar5','foobar5')])
      user_id = SelectField('User',validators=[required()],choices=[('1','1'),('2','2'),('3','3'),('4','4'),('5','5')])
    

    views :

    @app.route('/add/',methods=['GET','POST'])
    @login_required
    def new_task():
        form = AddTask(request.form,csrf_enabled=True)
        if request == 'POST' and form.validate():
            form_tasks = Tasks()
            form.populate_obj(form_tasks)
            db.session.add(form_tasks)
            db.session.commit()
            return redirect(url_for('tasks'))
        return render_template('form.html',form=form)
    

    form.html :

    {% extends "layout2.html" %}
    {% from 'common.html' import edit_field %}
    {%  block content %}
    <div class='edit well offset 2 span8'>
      <form method='post' class="form-horizontal">
        <legend> Add task </legend>
            {{ edit_field(form.name , class="span3") }}
            {{ edit_field(form.due_date , class="span3", type="datetime") }}
            {{ edit_field(form.priority, class="span3") }}
            {{ edit_field(form.description, rows="5" ,class="span3", placeholder="foobar" ) }}
            {{ edit_field(form.posted_date, class="span3",type="datetime") }}
            {{ edit_field(form.category, class="span3") }}
            {{ edit_field(form.super_category, class="span3") }}
    <div class="form-actions">
    <button type="submit" class="btn">SAVE</button>
    </div>
    </form>
    </div>
    {%  endblock    %}
    

    common.html :

    {% macro edit_field(field,catch_kwargs=true) %}
    <div class="control-group{% if field.errors  %} error {% endif %}">
        {{  field.label(class="control-label") }}
    <div class="controls">
        {{  field(**kwargs)  }}
        {% for error in field.errors %}
        <span class="help-inline">{{error}}</span>
        {% endfor %}
    </div>
    </div>
    {% endmacro %}
    
  • jwogrady
    jwogrady about 10 years
    Hi @Mammoth, welcome to the community. I see you are new here so wanted to suggest that the polite thing to do is to accept vittore's answer. I believe you can even update his response with your solution.
  • Grunt
    Grunt about 10 years
    Hi @jwogrady , i accepted Vittore's answer - but i don't know how to update his response :))