Best examples of examples of Django model relationships example in real projects

If you’re building anything serious with Django, you’ll hit model relationships almost immediately. Instead of another dry API recap, this guide walks through practical, opinionated examples of examples of Django model relationships example patterns that actually show up in real-world apps. We’ll look at an example of each core relationship type, talk about when you’d use them, and highlight gotchas that trip up even experienced developers. You’ll see how to wire up foreign keys, many-to-many fields, and one-to-one links in realistic scenarios like blogs, ecommerce, SaaS billing, and social features. These examples include code you can paste into a models.py file today, plus notes on migrations, performance, and how things have evolved in Django 4.x and 5.x. By the end, you’ll have a mental library of the best examples of Django model relationships, so the next time you’re sketching a schema, you’ll know exactly which pattern to reach for—and which to avoid.
Written by
Jamie
Published

Starting with real examples of Django model relationships

Let’s skip theory and jump straight into concrete code. The best examples of Django model relationships all come from the same place: problems you actually hit in production.

Below are several examples of examples of Django model relationships example patterns you’ll see over and over:

  • A blog with posts and comments
  • An ecommerce store with products, orders, and order items
  • A SaaS app with users, profiles, and subscriptions
  • A social feature with followers and likes
  • A tagging system that stays flexible over time
  • A multi-tenant setup where data is scoped to an organization

Each one leans on a different relationship type: ForeignKey, ManyToManyField, and OneToOneField, plus some advanced tricks that use through models and constraints.


Blog: classic ForeignKey one-to-many example of Post → Comment

This is usually the first relationship you write in Django, and it’s still one of the best examples of how simple the ORM can be.

from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    title = models.CharField(max_length=200)
    body = models.TextField()
    published_at = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.title


class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
    author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Comment by {self.author or 'Anonymous'} on {self.post}"

Why this matters in 2024–2025:

  • related_name is non-negotiable if you care about readable code and avoiding clashes.
  • Using get_user_model() instead of auth.User keeps you compatible with custom user models, which are now the norm in modern Django projects.

This is a clean example of a one-to-many relationship: one Post has many Comment objects; each Comment belongs to exactly one Post.


Ecommerce: the best examples of many-to-one and one-to-many combined

Real ecommerce schemas are a gold mine of examples of Django model relationships. You almost always end up with an Order that contains multiple OrderItem rows, each tied to a Product.

class Product(models.Model):
    name = models.CharField(max_length=200)
    sku = models.CharField(max_length=50, unique=True)
    price_cents = models.PositiveIntegerField()

    def __str__(self):
        return self.name


class Order(models.Model):
    customer = models.ForeignKey(User, on_delete=models.PROTECT, related_name="orders")
    created_at = models.DateTimeField(auto_now_add=True)
    paid = models.BooleanField(default=False)

    def __str__(self):
        return f"Order #{self.id} for {self.customer}"


class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items")
    product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="order_items")
    quantity = models.PositiveIntegerField(default=1)
    unit_price_cents = models.PositiveIntegerField()

    def line_total_cents(self):
        return self.quantity * self.unit_price_cents

This gives you two clear examples of one-to-many relationships:

  • One Order → many OrderItem rows
  • One Product → many OrderItem rows across different orders

Design choices worth copying:

  • on_delete=models.PROTECT on Product and customer prevents accidental data loss.
  • Storing unit_price_cents on OrderItem freezes the price at purchase time, which matches how real payment systems behave.

If you’re curious about how production-grade ecommerce deals with data integrity, payment providers like Stripe discuss similar modeling patterns in their docs: https://stripe.com/docs (not .gov/.edu, but highly relevant reading).


Many-to-many with extra data: tagging as a real example of a through model

Plain ManyToManyField is fine when you only care that two things are linked. But real apps often need extra metadata on the relationship itself. Tagging is one of the best examples of that.

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

    def __str__(self):
        return self.name


class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    tags = models.ManyToManyField(Tag, through="ArticleTag", related_name="articles")

    def __str__(self):
        return self.title


class ArticleTag(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
    added_at = models.DateTimeField(auto_now_add=True)
    added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

    class Meta:
        unique_together = ("article", "tag")

This is a textbook example of using through to store relationship-level data. You get:

  • When the tag was added
  • Who added it
  • A guarantee that an article–tag pair only appears once (unique_together)

In 2024–2025, you might replace unique_together with UniqueConstraint for more explicit naming and better migrations:

from django.db.models import UniqueConstraint

class ArticleTag(models.Model):
#    # fields as above

    class Meta:
        constraints = [
            UniqueConstraint(fields=["article", "tag"], name="uniq_article_tag_pair"),
        ]

This pattern scales well and is one of the best examples of a flexible design you won’t regret later.


One-to-one: profile and user, a real example from almost every SaaS app

If you need extra per-user data but want a clean separation from authentication, OneToOneField is your friend.

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
    timezone = models.CharField(max_length=50, default="UTC")
    marketing_opt_in = models.BooleanField(default=False)

    def __str__(self):
        return f"Profile for {self.user}"

This is a straightforward example of a one-to-one relationship:

  • Every User has at most one Profile.
  • Every Profile belongs to exactly one User.

Why teams still use this pattern heavily in 2024–2025:

  • It keeps your authentication model lean and focused.
  • It lets you split concerns across apps (accounts, billing, profiles) without massive migrations.

If you’re modeling sensitive user data (health, education, etc.), you’ll also want to read up on privacy and security best practices. For example, the U.S. Department of Health & Human Services has solid guidance on handling protected health information: https://www.hhs.gov/hipaa/index.html.


Subscriptions: examples include plan history and soft one-to-many

Modern SaaS billing is a rich source of examples of Django model relationships. You usually need to track subscription plans over time, not just the current one.

class Plan(models.Model):
    name = models.CharField(max_length=100)
    price_cents = models.PositiveIntegerField()
    interval = models.CharField(max_length=10, choices=[("month", "Month"), ("year", "Year")])

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


class Subscription(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="subscriptions")
    plan = models.ForeignKey(Plan, on_delete=models.PROTECT, related_name="subscriptions")
    started_at = models.DateTimeField()
    ended_at = models.DateTimeField(null=True, blank=True)

    @property
    def is_active(self):
        return self.ended_at is None

Here, examples include:

  • A user with multiple Subscription rows over time (plan upgrades, downgrades, cancellations).
  • A Plan used by many subscriptions, but protected from deletion.

This pattern plays nicely with external billing providers and analytics because you never lose history. It’s a very practical example of designing for time-based data, which matters a lot more now that teams care about churn and cohort analysis.


Social features: followers and likes as many-to-many real examples

Social graphs are another set of examples of Django model relationships that show up in dashboards, communities, and internal tools.

Followers: self-referential many-to-many

class SocialProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="social_profile")
    bio = models.CharField(max_length=160, blank=True)
    followers = models.ManyToManyField(
        "self",
        symmetrical=False,
        related_name="following",
        through="Follow",
    )


class Follow(models.Model):
    follower = models.ForeignKey(SocialProfile, on_delete=models.CASCADE, related_name="following_rel")
    followed = models.ForeignKey(SocialProfile, on_delete=models.CASCADE, related_name="followers_rel")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["follower", "followed"], name="uniq_follow_pair"),
            models.CheckConstraint(
                check=~models.Q(follower=models.F("followed")),
                name="prevent_self_follow",
            ),
        ]

This is one of the best examples of a self-referential many-to-many relationship with extra data and constraints.

Likes: many-to-many to content

class PostLike(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="post_likes")
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="likes")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ("user", "post")

You could use a ManyToManyField with through=PostLike, but keeping it explicit is often clearer in larger codebases.


Multi-tenant data: organization-scoped relationships example

As more teams ship SaaS products, multi-tenancy has become a default concern. That makes it a great place to look for examples of examples of Django model relationships example patterns that scale.

class Organization(models.Model):
    name = models.CharField(max_length=200)
    owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name="owned_organizations")

    def __str__(self):
        return self.name


class Membership(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="memberships")
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="memberships")
    role = models.CharField(max_length=50, default="member")

    class Meta:
        unique_together = ("user", "organization")


class Project(models.Model):
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name="projects")
    name = models.CharField(max_length=200)

    def __str__(self):
        return self.name

Here, examples include:

  • Users belonging to multiple organizations (Membership as a many-to-many through model).
  • Projects scoped to a single Organization via ForeignKey.

This structure keeps data isolation clear and is one of the best examples of a pattern that works for everything from analytics tools to internal CRMs.


Performance and query patterns for these Django relationship examples

All these examples of Django model relationships share a common performance story:

  • Use select_related for one-to-one and many-to-one (foreign keys) when you know you’ll need the related object.
  • Use prefetch_related for many-to-many and reverse foreign keys.
## Example: fetching posts with authors and comments efficiently
posts = (
    Post.objects
    .select_related("author")
    .prefetch_related("comments__author")
)

In 2024–2025, Django’s ORM has matured to the point where you can push quite far with these tools before reaching for raw SQL. For deeper reading on relational modeling and query optimization, classic database texts from universities still hold up well. For example, MIT’s open courseware on databases is a solid reference: https://ocw.mit.edu/courses/6-830-database-systems-fall-2010/


FAQ: short answers with more examples

What are some common examples of Django model relationships in real apps?

Common examples of Django model relationships include:

  • PostComment with a ForeignKey
  • OrderOrderItem for ecommerce
  • UserProfile with a OneToOneField
  • ArticleTag via a ManyToManyField and a through model
  • OrganizationUser via a Membership model

All of these are real examples you can adapt directly.

Can you give an example of when to use ManyToManyField vs a separate model?

Use a plain ManyToManyField when you only care that two records are linked, like a User subscribing to Newsletter topics. If you need extra data on the relationship (timestamps, roles, flags), use a separate model with ForeignKey fields and either declare it as through= or treat it explicitly, as in the ArticleTag and Membership examples above.

What is a good example of a one-to-one relationship besides user profiles?

A strong example of a one-to-one relationship is splitting sensitive data from a main record. For instance, Patient and PatientMedicalRecord in a health app, where the medical record is tightly controlled and audited separately. Health-focused organizations like the NIH discuss data separation and privacy in their research guidance: https://www.nih.gov/health-information.

How do these examples of Django model relationships scale as my app grows?

These patterns scale well as long as you:

  • Add indexes on foreign keys and frequently filtered fields.
  • Use select_related and prefetch_related to avoid N+1 queries.
  • Keep write-heavy models smaller and push archival data into separate tables when needed.

If you follow the best examples shown here—clear related_names, explicit on_delete behavior, and thoughtful constraints—you can grow from a side project to a serious production system without constantly rewriting your schema.

Explore More Django Code Snippets

Discover more examples and insights in this category.

View All Django Code Snippets