Many To Many relationship in Flask

Many To Many relationship in Flask App using SQLAlchemy Flask-WTForm and QuerySelectMultipleField

In this example we'll be using two entities, post and categories for whom we'll be building a many to many relationship.

The many to many relationship adds an association table between the two model classes, this helper table is strongly recommended to not be a model but an actual table.

Let's start with our models:

categories = db.Table('categories',
                  db.Column('post_id', db.Integer, db.ForeignKey(''), primary_key=True),
                  db.Column('category_id', db.Integer, db.ForeignKey(''), primary_key=True))

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(250))
    categories = db.relationship('Category', secondary=categories,
                                  backref=db.backref('posts', lazy='subquery'))

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    category = db.Column(db.String(140))

The association table categories is a super simple table containing just two columns both ForeignKey references to the id's of the post and the category representing the connection between the two.

However just having an association table is not enough to connect the two entities together so the categories attribute in the Post model is added. Since this is a many to many relationship a secondary table is needed to be specified in the relationship, this is a reference to the association table. The back reference backref can be visualised like adding another field to the Category model.Lazy is set to subquery which just joins the two tables.

Remember to migrate and update your db after making alterations. flask db migrate -m "many to many"

flask db upgrade

Next let's move on to our forms. We'll be focusing on the form for creating a post and associating many categories to it.

def possible_categories():
    return Category.query.all()

class PostForm(FlaskForm):
    body = TextAreaField('Say something', validators=[DataRequired()])
    categories = QuerySelectMultipleField('Categories', query_factory=possible_categories, get_label='category')
    submit = SubmitField('Submit')

How the QuerySelectMultipleField field works is that it will display a select drop-down field to choose between ORM results in a sqlalchemy query. For this the query_factory needs to be set to either a query or a function that returns ORM results, in our example the function possible_categories() is defined above that returns all the categories that are currently defined. For displaying we're using the category label as that will hold the category's name.

Moving on to the view function:

@app.route('/create_post', methods=['GET', 'POST'])
def create_post():
    form = PostForm()
    if request.method == 'POST':
        if form.validate_on_submit():
            new_post = Post(

            for categories in

            flash('New post added successfully!')
            flash('Blog post was not added.')
    return render_template('create_post.html', form=form)

Nothing too fancy here, we create a new post with the data inputted in the form, and then append each of the selected categories to it and save it in the db.

Lastly the html file. Simply:


<h1> Create A new blog post</h1>
<div class="row">
    <div class="col-md-4">
        {{ wtf.quick_form(form) }}

The implementation of the create category form and view goes along the same lines so I'll leave it for the readers homework.

Posted by Horia Gug

Comments Section

  • Horia Gug commented :

    Test Comment

    Testing comments

    Hey does code work in comment? maybe

Horia's Blog

Welcome to my blog! On here you will find random tech articles containing information about things i've learned while working, and other random stuff, enjoy.


You can find me on GitHub, or via email at:

Thank you for visiting and enjoy!

Please Log in or Signup to leave comments