A Complete Guide to Many‑to‑Many Relationships in Django with Practical Examples

This article explains how to define and work with many‑to‑many (M2M) relationships in Django using ManyToManyField. It covers creating related objects, adding and removing relationships, querying across M2M relations, reverse lookups, using set() and clear(), handling deletions, and understanding how Django manages M2M relations internally. All concepts are demonstrated with practical Python API examples.

ManyToManyField, Django ORM, M2M relationships, add()remove(), set(), clear()reverse relations, article_set

~2 min read · Updated Mar 10, 2026

1. Defining a Many‑to‑Many Relationship

To define a many‑to‑many relationship, Django provides ManyToManyField. In the example below, an Article can appear in multiple Publication objects, and each Publication can contain multiple Articles.


class Publication(models.Model):
    title = models.CharField(max_length=30)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)

2. Creating Data

Creating Publication instances:


p1 = Publication(title="The Python Journal"); p1.save()
p2 = Publication(title="Science News"); p2.save()
p3 = Publication(title="Science Weekly"); p3.save()

Creating an Article:


a1 = Article(headline="Django lets you build web apps easily")

You cannot add M2M relations before saving:


a1.publications.add(p1)  # Error

Save first:


a1.save()
a1.publications.add(p1)

3. Adding Multiple Relations


a2 = Article(headline="NASA uses Python"); a2.save()
a2.publications.add(p1, p2)
a2.publications.add(p3)

Adding duplicates is safe:


a2.publications.add(p3)

Adding the wrong type raises an error:


a2.publications.add(a1)  # TypeError

4. Creating and Adding in One Step


new_pub = a2.publications.create(title="Highlights for Children")

5. Accessing Related Objects

From Article → Publication:


a1.publications.all()
a2.publications.all()

From Publication → Article (reverse relation):


p1.article_set.all()
p2.article_set.all()

6. Querying Many‑to‑Many Relationships

Filtering by ID or object:


Article.objects.filter(publications__id=1)
Article.objects.filter(publications=p1)

Filtering by fields on the related model:


Article.objects.filter(publications__title__startswith="Science")

Use distinct() to avoid duplicates:


Article.objects.filter(publications__title__startswith="Science").distinct()

Filtering with lists:


Article.objects.filter(publications__in=[p1, p2]).distinct()

7. Reverse M2M Queries


Publication.objects.filter(article__headline__startswith="NASA")
Publication.objects.filter(article__in=[a1, a2]).distinct()

8. Removing Relations

From the Article side:


a4.publications.remove(p2)

From the Publication side:


p2.article_set.remove(a5)

9. Using set() and clear()

Setting the entire relation:


a4.publications.set([p3])

Clearing all relations:


a4.publications.clear()
p2.article_set.clear()

10. Behavior When Deleting Objects

Deleting a Publication:


p1.delete()
a1.publications.all()  # Empty

Deleting an Article:


a2.delete()
p2.article_set.all()  # Empty

11. Bulk Deletes and M2M Cleanup

Bulk delete Publications:


Publication.objects.filter(title__startswith="Science").delete()
a2.publications.all()  # Only remaining items

Bulk delete Articles:


q = Article.objects.filter(headline__startswith="Django")
q.delete()
q.all()  # Empty

Conclusion

Django’s ManyToManyField provides a powerful and flexible way to model complex relationships. With methods like add(), remove(), set(), clear(), and create(), you can fully manage M2M relations. Django also supports rich querying from both sides of the relationship and handles cleanup automatically when objects are deleted.

Written & researched by Dr. Shahin Siami

Related Articles

Django Tasks Framework: A Complete Guide to Background Task Execution in Django 6.0

Django 6.0 introduces the Tasks framework, a built‑in system for defining and queuing background work outside the request–response cycle. This article explains how Tasks work, how to configure backends, how to define and enqueue tasks, how context works, and how to integrate third‑party worker systems for production environments.

Continue

Asynchronous Support in Django: A Complete Guide to Async Views, ORM, Middleware, Performance, and Safety

This article explains Django’s asynchronous (async) capabilities, including async views, ASGI support, middleware behavior, async ORM features, performance considerations, handling disconnects, and Django’s async safety protections. It also covers how to use sync_to_async(), async ORM methods, and how to safely run synchronous code in async environments.

Continue

Django System Check Framework: A Complete Guide to Writing, Registering, Running, and Testing System Checks

This article explains Django’s System Check Framework—a powerful mechanism for detecting configuration issues, validating project structure, and ensuring code quality. It covers how checks are executed, how to write custom checks, how messages work, how to register and tag checks, how to extend checks for fields and models, and how to write both unit and integration tests for system checks.

Continue

Django Signals: A Complete Guide to Listening, Connecting, Sending, and Managing Application Events

This article provides a comprehensive explanation of Django’s signal system—an event‑driven mechanism that allows decoupled applications to react to actions occurring elsewhere in the framework. It covers how to define receivers, connect signals, use decorators, handle specific senders, organize signal code, and follow best practices to avoid complexity.

Continue

Understanding Django Settings: Configuration, Environment Management, and Best Practices

This article provides a complete overview of Django’s settings system. It explains how settings files work, how to designate a settings module, how to use settings in your code, how to configure Django manually, how to secure sensitive settings, and how to work with custom default settings. It also covers the role of django.setup() for standalone scripts.

Continue

Serializing and Deserializing Django Objects: A Complete Guide to Django’s Serialization Framework

This article explains Django’s serialization framework, including how to serialize and deserialize model instances, work with subsets of fields, handle inherited models, use different serialization formats (JSON, XML, YAML, JSONL), and understand how relational fields are represented. It also covers DeserializedObject behavior and common pitfalls.

Continue