BudiBadu Logo
Samplebadu

Django by Example: Many To Many Mapping

Django 5.0+

Many-to-Many relationships occur when multiple records in one table are associated with multiple records in another. This sample code shows how Django handles the intermediate "join table" automatically, or allows you to define a custom one.

Code

from django.db import models

class Tag(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    
    # Simple ManyToMany
    tags = models.ManyToManyField(Tag, related_name='articles')

# Custom Intermediate Model (Through Model)
class Student(models.Model):
    name = models.CharField(max_length=100)

class Course(models.Model):
    name = models.CharField(max_length=100)
    # Define the relationship through a custom model
    students = models.ManyToManyField(
        Student, 
        through='Enrollment',
        related_name='courses'
    )

class Enrollment(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    date_enrolled = models.DateField()
    grade = models.CharField(max_length=2, blank=True)

    class Meta:
        unique_together = [['student', 'course']]

# Usage:
# article = Article.objects.create(title="Django Tips")
# tag1 = Tag.objects.create(name="Python")
# article.tags.add(tag1) # Add relationship
#
# Custom Through:
# s1 = Student.objects.create(name="Bob")

Explanation

Many-to-Many relationships are used when an item in one table can be related to multiple items in another, and vice-versa. Django handles this by creating an intermediate "join table" automatically. However, if you need to store extra data about the relationship itself (e.g., date_enrolled or grade), you must define a custom intermediate model using the through argument.

Key considerations:

  • Symmetrical: By default, ManyToManyFields on the same model are symmetrical (if I am your friend, you are mine). You can disable this with symmetrical=False.
  • Prefetching: Because these relationships span multiple tables, they are prone to performance issues. Always use prefetch_related() (not select_related) to optimize queries.
  • Through Models: When using a custom through model, you cannot use the simple add(), create(), or remove() methods. You must create instances of the intermediate model directly.

Code Breakdown

27-32
constraints allows defining database-level constraints like CHECK constraints. This ensures data integrity at the database level, regardless of the application logic.