Jump into concepts and practice - no test required
or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Through model for extra fields on M2M
📖 Scenario: You are building a simple Django app to manage books and authors. Each book can have multiple authors, and each author can write multiple books. You want to store extra information about the relationship, such as the role of the author for that book (e.g., 'Writer', 'Editor').
🎯 Goal: Create a Django many-to-many relationship between Book and Author models using a through model to store the extra field role. This will allow you to track the role of each author on each book.
📋 What You'll Learn
Create Author and Book models with basic fields
Create a BookAuthor through model with a role field
Set up the many-to-many field on Book using the through model
Demonstrate adding authors to books with roles
💡 Why This Matters
🌍 Real World
Many real-world apps need to store extra data about relationships, like roles of people in projects or tags with metadata.
💼 Career
Understanding through models is essential for Django developers to handle complex many-to-many relationships with extra fields.
Progress0 / 4 steps
1
Create the basic Author and Book models
Create two Django models: Author with a name field as models.CharField(max_length=100), and Book with a title field as models.CharField(max_length=200). Do not add any relationships yet.
Django
Hint
Use models.CharField for text fields with max_length.
2
Create the BookAuthor through model with a role field
Create a new model called BookAuthor with three fields: book as a ForeignKey to Book, author as a ForeignKey to Author, and role as a models.CharField(max_length=50) to store the author's role on the book.
Django
Hint
Use ForeignKey with on_delete=models.CASCADE for relations.
3
Add the many-to-many field on Book using the through model
In the Book model, add a many-to-many field called authors that links to Author using the BookAuthor model as the through argument.
Django
Hint
Use models.ManyToManyField with the through parameter set to the through model name as a string.
4
Add example code to create a book with authors and roles
Write example code to create an Author named 'Jane Doe', a Book titled 'Django Basics', and then create a BookAuthor instance linking them with the role 'Writer'.
Django
Hint
Use Model.objects.create() to create instances and link them via the through model.
Practice
(1/5)
1. What is the main purpose of using a through model in a Django many-to-many relationship?
easy
A. To avoid using foreign keys in models
B. To speed up database queries automatically
C. To create a one-to-one relationship instead
D. To add extra fields to the relationship between two models
Solution
Step 1: Understand many-to-many relationships
A many-to-many field connects two models but by default stores only the link without extra data.
Step 2: Purpose of a through model
A through model is a separate model that stores the connection plus extra fields about that connection.
Final Answer:
To add extra fields to the relationship between two models -> Option D
Quick Check:
Through model = extra fields on M2M [OK]
Hint: Through model = extra info on many-to-many link [OK]
Common Mistakes:
Thinking through model speeds up queries
Confusing through model with one-to-one relationships
Believing through model removes foreign keys
2. Which of the following is the correct way to declare a many-to-many field using a through model named Membership in Django when the Membership model is defined later?
easy
A. members = models.ManyToManyField(User, through='Membership')
B. members = models.ManyToManyField(User, through=Membership())
C. members = models.ManyToManyField(User, through=Membership)
D. members = models.ManyToManyField(User, through='membership')
Solution
Step 1: Syntax for through argument
The through argument expects the model name as a string if the model is defined later or in the same app.
Step 2: Correct usage
Using 'Membership' as a string is correct. Passing the class or instance directly is incorrect.
Final Answer:
members = models.ManyToManyField(User, through='Membership') -> Option A
Quick Check:
through='ModelName' string syntax [OK]
Hint: Use model name as string in through argument [OK]
Common Mistakes:
Passing model class or instance instead of string
Using lowercase model name string
Omitting the through argument
3. Given the models below, what will print(membership.role) output?
class Group(models.Model):
name = models.CharField(max_length=100)
class User(models.Model):
username = models.CharField(max_length=100)
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
role = models.CharField(max_length=50)
# Usage
user = User(username='alice')
user.save()
group = Group(name='Developers')
group.save()
membership = Membership(user=user, group=group, role='admin')
membership.save()
print(membership.role)
medium
A. Error: role field missing
B. alice
C. admin
D. Developers
Solution
Step 1: Understand Membership model fields
Membership has a role field storing a string like 'admin'.
Step 2: Check the saved membership instance
Membership instance is created with role='admin', so printing membership.role outputs 'admin'.
Final Answer:
admin -> Option C
Quick Check:
membership.role = 'admin' [OK]
Hint: Print the extra field on through model instance [OK]
Common Mistakes:
Confusing role with user or group fields
Expecting username or group name instead
Assuming role field is missing
4. What is wrong with this through model declaration?
class User(models.Model):
username = models.CharField(max_length=100)
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey('Group', on_delete=models.CASCADE)
role = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=100)
members = models.ManyToManyField(User, through='Membership')
medium
A. Membership model is declared before Group, causing a NameError
B. No error; this is a valid declaration
C. The through model must be declared after both related models
D. ForeignKey fields in Membership must use related_name
Solution
Step 1: Check model declaration order
Membership can be declared before Group if the through argument uses string 'Membership'.
Step 2: Validate through usage
Using through='Membership' is correct and avoids circular import or NameError.
Final Answer:
No error; this is a valid declaration -> Option B
Quick Check:
through='ModelName' string allows any order [OK]
Hint: Use string name for through to avoid order errors [OK]
Common Mistakes:
Thinking model order causes NameError with string through
Believing related_name is mandatory for ForeignKey
Assuming through model must be after both models
5. You want to track the date a user joined a group using a through model. Which of these is the best way to add this feature?
hard
A. Add a date_joined = models.DateField() field to the through model and use through='Membership' in the many-to-many field
B. Add a date_joined field directly to the User model
C. Add a date_joined field directly to the Group model
D. Use a signal to store the date_joined in a separate table unrelated to the many-to-many
Solution
Step 1: Identify where to store extra relationship data
Extra info about the user-group link belongs in the through model, not in User or Group alone.
Step 2: Add date_joined field to through model
Adding date_joined to Membership and linking with through='Membership' is the correct pattern.
Final Answer:
Add a date_joined = models.DateField() field to the through model and use through='Membership' -> Option A
Quick Check:
Extra data on M2M = through model field [OK]
Hint: Extra data on M2M? Put field in through model [OK]
Common Mistakes:
Adding extra fields to User or Group instead of through model