Mr. Editor-in-chief Mr. Editor-in-chief September 15, 2021 Updated April 24, 2026

Clear Model Relationship API Usage

Django ORM could seem complex and daunting when different kinds of model relationships are combined together. To keep sanity, I devised a basic schema in differentiating different kind of model relationships.

Many-to-many relationships

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

    class Meta:
        ordering = ["title"]

    def __str__(self):
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publication_set = models.ManyToManyField(Publication, related_query_name='article_set')
    # By naming many to many field with _set suffix and setting 'related_query_name' with _set suffix,  I know both     article_obj.publication_set.all() and publication_obj.article_set.all() work and Article and Publication are ManyToMany-related to each other. Both Article.object.filter(publication_set__tile__istartswith='Science') and Publication.objects.filter(article_set__pk=1) work, too.

    class Meta:
        ordering = ["headline"]

    def __str__(self):
        return self.headline

Many-to-one relationships

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField()

    def __str__(self):
        return f"{self.first_name} {self.last_name}"


class Report(models.Model):
    headline = models.CharField(max_length=100)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter, related_name='reports', on_delete=models.CASCADE)
    # By set 'related_name' with 's' suffix or other plural form,  It is clear to me this is a one to many relation.
    # Now both reporter_obj.reports.all() and Reporter.objects.filter(reports__headline__icontains='This') work.

    def __str__(self):
        return self.headline

    class Meta:
        ordering = ["headline"]

One-to-one relationships

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):
        return f"{self.name} the place"


class Restaurant(models.Model):
    place = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
        primary_key=True,
    )
    # Queries such as restaurant_obj.place, place_obj.restaurant, Restaurant.objects.filter(place__name__startswith='Demon) and Place.objects.filter(restaurant__serves_pizza=True) will work.
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):
        return "%s the restaurant" % self.place.name


class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant, related_name='waiters', on_delete=models.CASCADE)
    name = models.CharField(max_length=50)

    def __str__(self):
        return "%s the waiter at %s" % (self.name, self.restaurant)

Please refer to Django Documentation: Examples of model relationship API usage for detailed query examples and try out yourself.