Many-to-many relationships let you connect two things where each can have many of the other. For example, a student can join many classes, and each class can have many students.
Many-to-many relationships in Flask
from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() association_table = db.Table('association', db.Column('left_id', db.Integer, db.ForeignKey('left.id')), db.Column('right_id', db.Integer, db.ForeignKey('right.id')) ) class Left(db.Model): id = db.Column(db.Integer, primary_key=True) rights = db.relationship('Right', secondary=association_table, back_populates='lefts') class Right(db.Model): id = db.Column(db.Integer, primary_key=True) lefts = db.relationship('Left', secondary=association_table, back_populates='rights')
The db.Table creates a helper table to link two models.
The secondary argument tells SQLAlchemy which table connects the two sides.
association_table = db.Table('student_course', db.Column('student_id', db.Integer, db.ForeignKey('student.id')), db.Column('course_id', db.Integer, db.ForeignKey('course.id')) ) class Student(db.Model): id = db.Column(db.Integer, primary_key=True) courses = db.relationship('Course', secondary=association_table, back_populates='students') class Course(db.Model): id = db.Column(db.Integer, primary_key=True) students = db.relationship('Student', secondary=association_table, back_populates='courses')
tags_posts = db.Table('tags_posts', db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')), db.Column('post_id', db.Integer, db.ForeignKey('post.id')) ) class Tag(db.Model): id = db.Column(db.Integer, primary_key=True) posts = db.relationship('Post', secondary=tags_posts, back_populates='tags') class Post(db.Model): id = db.Column(db.Integer, primary_key=True) tags = db.relationship('Tag', secondary=tags_posts, back_populates='posts')
This Flask app creates authors and books linked many-to-many. It shows how to add books to authors and print their relationships.
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) association_table = db.Table('association', db.Column('author_id', db.Integer, db.ForeignKey('author.id')), db.Column('book_id', db.Integer, db.ForeignKey('book.id')) ) class Author(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False) books = db.relationship('Book', secondary=association_table, back_populates='authors') class Book(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(100), nullable=False) authors = db.relationship('Author', secondary=association_table, back_populates='books') with app.app_context(): db.create_all() a1 = Author(name='Alice') a2 = Author(name='Bob') b1 = Book(title='Flask Basics') b2 = Book(title='Advanced Flask') a1.books.append(b1) # Alice wrote Flask Basics a2.books.extend([b1, b2]) # Bob wrote both books db.session.add_all([a1, a2, b1, b2]) db.session.commit() # Print authors and their books for author in Author.query.order_by(Author.name): book_titles = ', '.join(book.title for book in author.books) print(f"{author.name} wrote: {book_titles}")
Always create the association table before defining relationships.
Use back_populates on both sides to keep relationships in sync.
Remember to commit your session to save changes to the database.
Many-to-many relationships connect two models where each can have many of the other.
Use an association table with db.Table to link the two models.
Define relationships with secondary and back_populates for easy access both ways.
Practice
Solution
Step 1: Understand many-to-many relationships
Many-to-many means each record in one table can relate to many records in another table, and vice versa.Step 2: Role of association table
An association table connects these two tables by holding pairs of related record IDs, enabling the many-to-many link.Final Answer:
To link two tables that can have multiple related records each -> Option AQuick Check:
Association table = link many-to-many tables [OK]
- Thinking association tables store user credentials
- Confusing many-to-many with one-to-one relationships
- Assuming association tables optimize queries automatically
Solution
Step 1: Identify correct association table syntax
Association tables usedb.Tablewith columns having foreign keys to related tables.Step 2: Check options for correct use of
association = db.Table('association', db.Column('left_id', db.Integer, db.ForeignKey('left.id')), db.Column('right_id', db.Integer, db.ForeignKey('right.id'))) correctly usesdb.Tableand foreign keysdb.Tableand defines columns with foreign keys to 'left.id' and 'right.id'.Final Answer:
association = db.Table('association', db.Column('left_id', db.Integer, db.ForeignKey('left.id')), db.Column('right_id', db.Integer, db.ForeignKey('right.id'))) -> Option DQuick Check:
Association table = db.Table + foreign keys [OK]
- Using db.Model instead of db.Table for association tables
- Missing foreign key constraints in columns
- Using wrong data types like String for IDs
print(user.groups[0].name) output?association = db.Table('association',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
groups = db.relationship('Group', secondary=association, back_populates='users')
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
users = db.relationship('User', secondary=association, back_populates='groups')
user = User(name='Alice')
group = Group(name='Admins')
user.groups.append(group)
print(user.groups[0].name)Solution
Step 1: Understand relationship setup
User and Group are linked many-to-many via association table. User.groups holds Group objects.Step 2: Analyze code behavior
User 'Alice' has 'Admins' group appended. Accessing user.groups[0].name returns 'Admins'.Final Answer:
Admins -> Option BQuick Check:
user.groups[0].name = 'Admins' [OK]
- Confusing user name with group name
- Expecting None if not committed to DB
- Assuming error due to missing session commit
association = db.Table('association',
db.Column('user_id', db.Integer),
db.Column('group_id', db.Integer)
)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
groups = db.relationship('Group', secondary=association, back_populates='users')
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', secondary=association, back_populates='groups')Solution
Step 1: Check association table columns
Columns lack foreign key constraints linking to User and Group tables.Step 2: Importance of foreign keys
Foreign keys are required to enforce relationships and enable SQLAlchemy to join tables properly.Final Answer:
Missing foreign key constraints in association table columns -> Option CQuick Check:
Foreign keys required in association table [OK]
- Omitting foreign keys in association table
- Confusing back_populates usage
- Thinking primary key is missing in User model
role attribute to the association between User and Group models to store the user's role in each group. Which approach correctly implements this in Flask SQLAlchemy?Solution
Step 1: Understand limitation of db.Table
db.Table is a simple table without a model class, so it cannot hold extra attributes like 'role'.Step 2: Use association object pattern
Create a new model class representing the association with foreign keys and extra columns like 'role'. This allows storing additional data per relationship.Final Answer:
Create a new model class for the association with user_id, group_id, and role columns -> Option AQuick Check:
Extra data in many-to-many needs association model class [OK]
- Trying to add extra columns to db.Table association
- Adding role to User or Group instead of association
- Not using a model class for complex associations
Ready to prove it?
Pass the test and this pattern moves unknown
