🌐
Django
docs.djangoproject.com › en › 6.0 › topics › db › examples › many_to_many
Many-to-many relationships | Django documentation | Django
Get 💸✨30%✨💸 off a new annual PyCharm Professional license while supporting the foundation. All money goes to the Django Software Foundation! 🎁 Get PyCharm for 30% off ... To define a many-to-many relationship, use ManyToManyField.
🌐
Reddit
reddit.com › r/django › the right way to use a manytomanyfield in django
r/django on Reddit: The right way to use a ManyToManyField in Django
January 31, 2021 -

When you design a database for a large product, it is inevitable to arrive at a point where you have two models that are related to each other in a way that does not get solved using a ForeignKey alone. 

A good example of a many-to-many relationship is the relationship between a sandwich and a sauce. I like a chicken teriyaki sandwich but only if it contains barbeque sauce as well as mayonnaise sauce. So the same sandwich can have multiple sauces. At the same time I want mayonnaise sauce to appear on a turkey sandwich as well, so the same sauce can be used on different kinds of sandwiches.

This is a great place to use the ManyToManyField offered by Django instead of a regular ForeignKey. Unfortunately the way Django deals with this is a bit unintuitive and can be confusing, so I thought it would be best to demonstrate how a many to many relationship works under the hood.

Many-to-many relationship in a database

To maintain a many-to-many relationship between two tables in a database, the only way is to have a third table which has references to both of those tables. This table is called a “through” table and each entry in this table will connect the source table (sandwich in this case) and the target table (sauce in this case).

This is exactly what Django does under the hood when you use a ManyToManyField. It creates a through model which is not visible to the ORM user and whenever one needs to fetch all of the sandwiches that use a particular sauce given only the name of the sauce, the above 3 tables are joined.

Joining 3 tables may not be very efficient, so if you query this information using the sauce ID instead of name, Django internally Joins only 2 tables (sandwiches_sauces and sandwiches). These join operations are invisible to the user but it helps to know what’s going on in the database so that the queries can be made as efficient as possible.

Using the ManyToManyField

Now that we know what happens internally, let’s look at how the ManyToManyField helps abstract out all of this complexity and provides a simple interface to the person writing code.

Let us create a new Django project and an app within it called sandwiches. In models.py, define these two models.

from django.db import models

class Sauce(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Sandwich(models.Model):
    name = models.CharField(max_length=100)
    sauces = models.ManyToManyField(Sauce)

    def __str__(self):
        return self.name

And that’s it. Running a database migration on these models will create a Sandwich table, a Sauce table and a through table connecting the two. Let us now look at how we can access this information using the Django ORM.

Fetching all sandwiches and sauces using each other

Let us create some data in the Sandwich and Sauce models and see how we can retrieve them.

>>> chicken_teriyaki_sandwich = Sandwich.objects.create(name="Chicken Teriyaki Sandwich")
>>> bbq_sauce = Sauce.objects.create(name="Barbeque")
>>> mayo_sauce = Sauce.objects.create(name="Mayonnaise")
>>> 
>>> chicken_teriyaki_sandwich.sauces.add(bbq_sauce)
>>> chicken_teriyaki_sandwich.sauces.add(mayo_sauce)
>>> 
>>> chicken_teriyaki_sandwich.sauces.all()
<QuerySet [<Sauce: Barbeque>, <Sauce: Mayonnaise>]>
>>> 

Running sandwich.sauces.all() gives us all the sauces applied on that Sandwich but if we want to perform a reverse action, i.e get all the sandwiches that use a particular sauce, this can be done by performing the same operation on the target object by using a _set.

>>> bbq_sauce = Sauce.objects.get(name="Barbeque sauce")
>>> 
>>> bbq_sauce.sandwich.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Sauce' object has no attribute 'sandwich'
>>> 
>>> 
>>> bbq_sauce.sandwich_set.all()
<QuerySet [<Sandwich: Chicken Teriyaki>]>
>>> 

As  you can see, trying to perform a bbq_sauce.sandwich.all() threw a AttributeError but bbq_sauce.sandwich_set.all() worked. This is because Django internally references a target ManyToManyField as a set. This behaviour can be overridden by providing a related_name option to the target field.

class Sandwich(models.Model):
    name = models.CharField(max_length=100)
    sauces = models.ManyToManyField(Sauce, related_name="sandwiches")

    def __str__(self):
        return self.name

Now we can perform the previous query using sandwiches instead of sandwiches_set.

>>> 
>>> 
>>> bbq_sauce = Sauce.objects.get(name="Barbeque sauce")
>>> bbq_sauce.sandwiches.all()
<QuerySet [<Sandwich: Chicken Teriyaki>]>
>>> 
>>> 

In simpler words, a related name is the phrase that you would like to use instead of a *_set.

The recommended way to choose a related name is to use the plural form of your model name in lowercase.

Fetching specific sandwiches and sauces using each other

In the above examples, we were fetching all entries in the table using a .all() function, but in most practical cases, we would want to query a subset of the data. For instance we might want to query all the sandwiches that use barbeque sauce. This can be done with a query like this:

>>> 
>>> Sandwich.objects.filter(sauces__name="Barbeque sauce")
<QuerySet [<Sandwich: Chicken Teriyaki>]>
>>> 
>>> 

But like I mentioned before, to perform this query Django internally has to join all the 3 tables. We can make this more efficient by querying using the sauce ID instead of name. This will enable Django to join only the Sandwich table and the through table.

>>> 
>>> Sandwich.objects.filter(sauces__id=1)
<QuerySet [<Sandwich: Chicken Teriyaki>]>
>>> 
>>> 
>>> 

You can also query this information in reverse, i.e fetch all sauces that are put on a particular sandwich.

>>> 
>>> Sauce.objects.filter(sandwich__name="Chicken Teriyaki")
<QuerySet [<Sauce: Barbeque sauce>, <Sauce: Mayonnaise sauce>]>
>>> 
>>> 
>>> 

Even in this case I would recommend querying using the sandwich ID to make this query more efficient.

Adding Items from either side of the relationship

So far we have been adding sauces to the sandwich model but we can also go the other way round pretty easily. Django internally takes care of whatever database table entries need to be created to make this happen.

The only gotcha is that if you don’t plan to use a related_name, you would have to add the item to a *_set attribute.

>>> 
>>> 
>>> sandwich = Sandwich.objects.get(name="Turkey")
>>> 
>>> mayo_sauce = Sauce.objects.get(name="Mayonnaise sauce")
>>> 
>>> mayo_sauce.sandwich_set.add(sandwich)
>>> 
>>> 

Using a custom “through” model

Even though Django takes care of creating the through model on its own and keeps this invisible to a user, sometimes it becomes necessary to use a custom through model in order to add some additional fields to that model.

For instance consider the relationship between a student and a teacher. A teacher can teach multiple students and a student can be taught by multiple teachers thereby qualifying this for a many to many relationship. 

However in this case just having a table that connects these two entities won’t suffice because we would require extra information such as:

  • The date on which a teacher started teaching a student.

  • The subject that is taught by a teacher to a student.

  • Duration of the course.

To sum this up, we require a “course” table that not only connects a student and a teacher but also holds this extra information.

To make this happen, one must override the default though table that Django creates and use a custom through table instead.

Extra sauce please!

Ok enough talk about students and teachers, let’s get back into the topic that probably interested you in this blog post in the first place - food!

In all of the above examples of Sandwiches and sauces, the only information we have is what sauces go on what sandwiches but what if someone wants to put extra sauce of the same kind on a sandwich? 

You could try adding the same sauce model to a sandwich model multiple times but Django would simply ignore it as the add function is idempotent. You can add a particular sauce to a Sandwich only once. To solve this problem we can use a custom through model.

Note: Do keep in mind that if you want to go down this road you must do this from the start or be okay with dropping your database and starting from scratch because Django does not allow you to create a custom through model after previously using a default through model. If you try this you may see weird errors like this one:

raise ValueError(
ValueError: Cannot alter field sandwiches.Sandwich.sauces into sandwiches.Sandwich.sauces - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)

Creating a custom through model:

from django.db import models

class Sauce(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Sandwich(models.Model):
    name = models.CharField(max_length=100)
    sauces = models.ManyToManyField(Sauce, through='SauceQuantity')

    def __str__(self):
        return self.name

class SauceQuantity(models.Model):
    sauce = models.ForeignKey(Sauce, on_delete=models.CASCADE)
    sandwich = models.ForeignKey(Sandwich, on_delete=models.CASCADE)
    extra_sauce = models.BooleanField(default=False)

    def __str__(self):
        return "{}_{}".format(self.sandwich.__str__(), self.sauce.__str__())

With a custom through model you will not be able to add sauces to a Sandwich like you did before. Instead you would have to create entries of the SauceQuantity model explicitly as shown below.

>>> from sandwiches.models import *
>>> 
>>> 
>>> chicken_teriyaki_sandwich = Sandwich.objects.create(name="Chicken Teriyaki with mayo and extra bbq sauce")
>>> 
>>> 
>>> bbq_sauce = Sauce.objects.create(name="Barbeque")
>>> 
>>> SauceQuantity.objects.create(sandwich=chicken_teriyaki_sandwich, sauce=bbq_sauce, extra_sauce=True)
<SauceQuantity: Chicken Teriyaki with mayo and extra bbq sauce_Barbeque>
>>> 
>>> SauceQuantity.objects.create(sandwich=chicken_teriyaki_sandwich, sauce=mayo_sauce, extra_sauce=False)
<SauceQuantity: Chicken Teriyaki with mayo and extra bbq sauce_Mayonaisse>
>>>

You can still access a sauce from a sandwich and a sandwich from a sauce just like you previously did.

>>> 
>>> chicken_teriyaki_sandwich.sauces.all()
<QuerySet [<Sauce: Barbeque>, <Sauce: Mayonnaise>]>
>>> 
>>> bbq_sauce.sandwich_set.all()
<QuerySet [<Sandwich: Chicken Teriyaki with mayo and extra bbq sauce>]>
>>> 
>>> 

In order to know what all sauces are being used on a sandwich and in what quantities, we can iterate through the sauces of a Sandwich and retrieve information from the SauceQuantity model for each of the sauces as shown below.

>>> 
>>> 
>>> for sauce in chicken_teriyaki_sandwich.sauces.all():
...  saucequantity = SauceQuantity.objects.get(sauce=sauce, sandwich=chicken_teriyaki_sandwich)
...  print("{}{}".format("Extra " if saucequantity.extra_sauce else "", sauce))
... 
Extra Barbeque
Mayonnaise
>>> 

The SauceQuantity model can also be extended further to include stuff like whether or not the sandwich is cut in half, type of bread used, etc. 

Closing notes

The ManyToManyField may be confusing but it is very handy. Any type of confusion you may have can be resolved with the right documentation. Here are a few that really helped me.

  • The official Django documentation that covers many-to-many relationships which you can find here.

  • A great example on when to use a custom through model found here.

Originally posted on my blog

Discussions

If i am using through table in ManyToMany field what is point of having ManyToManyField?
Hello there, this is my first question in django forum. I have two modela Publication and Article as follow. class Publication(models.Model): title = models.CharField(max_length=30) class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyFie... More on forum.djangoproject.com
🌐 forum.djangoproject.com
0
1
April 24, 2020
Add multiple entries to many-to-many field with different through_defaults for each entry
Say I have a model A with a field called m2m which is a many-to-many field to another model B. Then I want to add all the elements inside a queryset to this field at once. If qs is my queryset, I could do a.m2m.add(*qs). However, say I’m using a through model for the relationship and that ... More on forum.djangoproject.com
🌐 forum.djangoproject.com
2
0
June 26, 2021
python - ManyToMany Relationship between two models in Django - Stack Overflow
The related managers that are added, etc. is all Django logic. Behind the curtains, it will query the table in the middle. You however need to fix the related_name=… parameter [Django-doc]. The related_name specifies the name of the relation in reverse so from Course to Profile in this case. More on stackoverflow.com
🌐 stackoverflow.com
Django Many to Many relationship
There is a many to many relationship in my model. Now my requirement is that as it is a many to many relationship I want to select some objects. For example there are 10 objects in the many to many field. Now if I select the 10th object and then the first it should show in the same order when ... More on forum.djangoproject.com
🌐 forum.djangoproject.com
1
0
March 27, 2024
🌐
Django Forum
forum.djangoproject.com › using django
Changing a ManyToManyField to use a through model? - Using Django - Django Forum
November 19, 2021 - Hi all. I feel puzzled when I see the code below. Why is it recommended to change the table name? I tried to delete the database_operations in the following code, and then change the table name of AuthorBook to core_book_authors in state_operations. It can still migrate successfully and add fields to the intermediate table.
🌐
Django Forum
forum.djangoproject.com › using django
If i am using through table in ManyToMany field what is point of having ManyToManyField? - Using Django - Django Forum
April 24, 2020 - Hello there, this is my first question in django forum. I have two modela Publication and Article as follow. class Publication(models.Model): title = models.CharField(max_length=30) class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication, related_name="published_article", through='articles_publication', ...
🌐
Django Forum
forum.djangoproject.com › using django
Add multiple entries to many-to-many field with different through_defaults for each entry - Using Django - Django Forum
June 26, 2021 - Say I have a model A with a field called m2m which is a many-to-many field to another model B. Then I want to add all the elements inside a queryset to this field at once. If qs is my queryset, I could do a.m2m.add(*qs). However, say I’m using a through model for the relationship and that ...
🌐
GeeksforGeeks
geeksforgeeks.org › python › how-to-add-multiple-objects-to-manytomany-relationship-at-once-in-django
How to Add Multiple Objects to ManyToMany Relationship at Once in Django? - GeeksforGeeks
July 23, 2025 - In Django, the ManyToManyField allows for the creation of relationships where multiple records in one table can be associated with multiple records in another table. Adding multiple objects to a Many-to-Many relationship is a common requirement ...
🌐
CodeSignal
codesignal.com › learn › courses › advanced-database-schema-design-in-django › lessons › many-to-many-relationship-basics
Many-to-many Relationship: Basics
You can add multiple tags to each todo item, and each tag can be added to multiple todo items. This is called a many-to-many relationship. We'll use two models: Tag and Todo. Each Todo task can have multiple Tags, and each Tag can be associated with multiple Todo tasks.
Find elsewhere
🌐
ZeroToByte
zerotobyte.com › home › django many to many relationship explained
Django Many To Many Relationship Explained - ZeroToByte
February 20, 2022 - Many to Many is a way of connecting entities with one additional table (associative table). Associative table points to entities it connects. The associative table contains primary keys of entities it connects and additional optional parameters.
🌐
Django
docs.djangoproject.com › en › 6.0 › ref › models › relations
Related objects reference | Django documentation | Django
Get 💸✨30%✨💸 off a new annual PyCharm Professional license while supporting the foundation. All money goes to the Django Software Foundation! 🎁 Get PyCharm for 30% off ... A “related manager” is a manager used in a one-to-many or many-to-many related context.
🌐
Medium
erdimollahseyin.medium.com › django-models-working-with-m2m-intermediary-models-3919ebe3a5cf
Mastering Many-to-Many Relationships in Django: A Guide to Intermediary Models | by Erdi Mollahüseyinoğlu | Medium
March 27, 2025 - # querysets Group.objects.filter(membership__invite_reason="foo") <QuerySet [<Group: Python>]> Group.objects.filter(membership__invite_reason="bar") <QuerySet [<Group: Django>]> Person.objects.filter(membership__invite_reason="foo") <QuerySet [<Person: Erdi>]> Person.objects.filter(membership__invite_reason="bar") <QuerySet [<Person: Erdi>]> Membership.objects.filter(invite_reason='foo') <QuerySet [<Membership: Erdi Python>]> Membership.objects.filter(invite_reason='bar') <QuerySet [<Membership: Erdi Django>]> # related manager on instances person_instance = Person.objects.first() person_instance.groups.filter(membership__invite_reason='foo') <QuerySet [<Group: Python>]> group_instance = Group.objects.first() group_instance.members.filter(membership__invite_reason='foo') <QuerySet [<Person: Erdi>]> In this article you’ve learned how to use a through with many-to-many relationship.
🌐
Velog
velog.io › @daylee › Django-Model-Many-to-Many-Relationships
Django Model: One-to-Many, Many-to-Many Relationships
ManyToManyField is used to specify a many-to-many relationship (e.g. a book can have several genres, and each genre can contain several books). In our library app we will use these very similarly to ForeignKeys, but they can be used in more ...
🌐
GeeksforGeeks
geeksforgeeks.org › python › how-to-express-a-one-to-many-relationship-in-django
How to Express a One-To-Many Relationship in Django? - GeeksforGeeks
July 23, 2025 - In Django, expressing relationships between models is crucial to structuring our database. One of the most common relationships is One-To-Many, where a single record in one model is associated with multiple records in another model.
Top answer
1 of 2
16

For a relational databases, the model where you define the ManyToManyField does not matter. Django will create an extra table with two ForeignKeys to the two models that are linked by the ManyToManyField.

The related managers that are added, etc. is all Django logic. Behind the curtains, it will query the table in the middle.

You however need to fix the related_name=… parameter [Django-doc]. The related_name specifies the name of the relation in reverse so from Course to Profile in this case. It thus should be something like 'profiles':

class Profile(AbstractUser):
    bio = models.TextField()
    image = models.ImageField(default='defaults/user/default_u_i.png', 
    courses = models.ManyToManyField('home.Course', related_name='profiles')

    def __str__(self):
        return self.username

You thus can obtain the people that particiate in a Course object with:

mycourse.profiles.all()

and you can access the courses in which a Profile is enrolled with:

myprofile.courses.all()

For more information, see the Many-to-many relationships section of the documentation.

You can add a course to the courses of a user with:

@login_required
def course_add(request):
    if request.method == 'POST':
        form = CourseForm(request.POST)
        if form.is_valid():
            course = form.save()
            request.user.courses.add(course)
    else:
        form = CourseForm()
    context = {
        'form': form
    }
    return render(request,'home/courses/course_add.html', context)
2 of 2
0

You don't need to add the related name. Default is "courses_set" in your case. Here is excerpt from: https://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-related-objects

Following relationships “backward” If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the “Retrieving objects” section above.

🌐
Django Forum
forum.djangoproject.com › using django › using the orm
Django Many to Many relationship - Using the ORM - Django Forum
March 27, 2024 - There is a many to many relationship in my model. Now my requirement is that as it is a many to many relationship I want to select some objects. For example there are 10 objects in the many to many field. Now if I select the 10th object and then the first it should show in the same order when ...
🌐
Sankalpjonna
sankalpjonna.com › learn-django › the-right-way-to-use-a-manytomanyfield-in-django
The right way to use a ManyToManyField in Django
To maintain a many-to-many relationship between two tables in a database, the only way is to have a third table which has references to both of those tables. This table is called a “through” table and each entry in this table will connect the source table (sandwich in this case) and the ...
🌐
Django
docs.djangoproject.com › en › 6.0 › topics › db › queries
Making queries | Django documentation | Django
When you define a relationship in a model (i.e., a ForeignKey, OneToOneField, or ManyToManyField), instances of that model will have a convenient API to access the related object(s). Using the models at the top of this page, for example, an Entry object e can get its associated Blog object by accessing the blog attribute: e.blog. (Behind the scenes, this functionality is implemented by Python descriptors. This shouldn’t really matter to you, but we point it out here for the curious.) Django also creates API accessors for the “other” side of the relationship – the link from the related model to the model that defines the relationship.
🌐
Django Forum
forum.djangoproject.com › using django
How do I set the value of a Many to Many field programmatically? - Using Django - Django Forum
January 2, 2021 - I am trying to set the value of a tag to “Bought” but I just can’t figure it out. background : I have created a form to buy stock and when a person buys stock I want the value of that trade to be set to “bought”, but I just can’t figure it out. I am putting some code below (not al of it, just the part you might need) The Bold part is where I have a problem. the model : class Trade(models.Model): trader = models.ForeignKey(User_Info, null=True, on_delete = models.SET_NULL ) ticker...
🌐
Django
docs.djangoproject.com › en › 6.0 › topics › db › examples › many_to_one
Many-to-one relationships | Django documentation | Django
Get 💸✨30%✨💸 off a new annual PyCharm Professional license while supporting the foundation. All money goes to the Django Software Foundation! 🎁 Get PyCharm for 30% off ... To define a many-to-one relationship, use ForeignKey.
🌐
Django
django.how › models › manytomany-relationship
ManyToMany Relationship Django.How
class Sample(models.Model): users = models.ManyToManyField(User, blank=True) # if not blank=True it will not be required, null=True is meaningless