SyntaxStudy
Sign Up
Django Model Relationships and Meta Options
Django Beginner 1 min read

Model Relationships and Meta Options

Django supports three main relationship types between models. ForeignKey creates a many-to-one relationship — for example many posts belonging to one author. ManyToManyField creates a many-to-many relationship — for example posts having multiple tags. OneToOneField creates a one-to-one relationship — for example a user profile extending the built-in User model. Each relationship type generates the appropriate SQL foreign keys and junction tables automatically. The related_name argument on relationship fields controls the reverse accessor name. Setting related_name='posts' on a ForeignKey(User) field means you can access a user's posts via user.posts.all() rather than the default user.post_set.all(). Setting related_name='+' disables the reverse accessor if you don't need it. The inner Meta class on a model customises its behaviour. Common Meta options include ordering (default sort order), unique_together and constraints (enforce multi-column uniqueness or check constraints), verbose_name and verbose_name_plural (human-readable names for the admin), db_table (custom table name), and abstract=True (to create a base class that is not materialised as its own table).
Example
# blog/models.py — relationships and Meta options
from django.db import models
from django.contrib.auth.models import User


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

    def __str__(self):
        return self.name


class UserProfile(models.Model):
    """One-to-one extension of the built-in User."""
    user    = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio     = models.TextField(blank=True)
    website = models.URLField(blank=True)

    def __str__(self):
        return f'Profile of {self.user.username}'


class Post(models.Model):
    author   = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    tags     = models.ManyToManyField(Tag, blank=True, related_name='posts')
    title    = models.CharField(max_length=200)
    body     = models.TextField()
    views    = models.PositiveIntegerField(default=0)

    class Meta:
        ordering       = ['-id']
        verbose_name   = 'Post'
        verbose_name_plural = 'Posts'
        constraints    = [
            models.UniqueConstraint(fields=['author', 'title'], name='unique_author_title'),
        ]
        indexes = [
            models.Index(fields=['author', '-id']),
        ]

    def __str__(self):
        return self.title


# Accessing relationships
# user.posts.all()           -> all posts by this user (reverse FK)
# post.tags.all()            -> all tags on this post
# tag.posts.filter(...)      -> all posts with this tag (reverse M2M)
# user.profile               -> OneToOne reverse accessor