Skip to content

1. Basics

In Django, models provide a powerful way to define and interact with your database. Advanced model usage in Django involves leveraging more complex features like relationships, custom managers, custom querysets, abstract models, signals, and model inheritance. Below are several examples of advanced model usage in Django.

1. Model Relationships

a. ForeignKey (One-to-Many)

A ForeignKey defines a one-to-many relationship between two models, where one instance of a model can be related to many instances of another model.

class Author(models.Model):
name = models.CharField(max_length=100)
birth_date = models.DateField()
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publication_date = models.DateField()
  • Explanation: A Book is related to an Author, meaning an author can have multiple books. The on_delete=models.CASCADE ensures that when an author is deleted, all their related books are also deleted.

b. Many-to-Many Relationship

class Course(models.Model):
name = models.CharField(max_length=100)
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course)
  • Explanation: A Student can take multiple Course instances, and each Course can have multiple Student instances. Django creates a join table automatically to manage this relationship.

c. One-to-One Relationship

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
class User(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
  • Explanation: A User can have only one Profile, and a Profile is tied to exactly one User.

2. Custom Model Managers

Django allows you to define custom model managers to encapsulate custom database queries.

class Book(models.Model):
title = models.CharField(max_length=100)
published_date = models.DateField()
# Custom Manager
class BookManager(models.Manager):
def published_books(self):
return self.filter(published_date__lte=datetime.date.today())
# Attach the manager
objects = models.Manager() # Default manager
published = BookManager() # Custom manager
  • Usage: You can now call Book.published.published_books() to get only the books that have been published.

3. Custom QuerySets

You can define custom methods on a queryset to simplify complex queries.

class BookQuerySet(models.QuerySet):
def published_books(self):
return self.filter(published_date__lte=datetime.date.today())
def upcoming_books(self):
return self.filter(published_date__gt=datetime.date.today())
class Book(models.Model):
title = models.CharField(max_length=100)
published_date = models.DateField()
objects = BookQuerySet.as_manager() # Use the custom queryset
  • Usage: You can now call Book.objects.published_books() to get books that have been published, or Book.objects.upcoming_books() for upcoming books.

4. Model Inheritance

a. Abstract Base Classes

Abstract base classes allow you to create a base class with common fields, and the derived classes will inherit these fields.

class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Blog(TimeStampedModel):
title = models.CharField(max_length=100)
content = models.TextField()
class Event(TimeStampedModel):
name = models.CharField(max_length=100)
event_date = models.DateField()
  • Explanation: Both Blog and Event will inherit created_at and updated_at from TimeStampedModel.

b. Multi-Table Inheritance

class Animal(models.Model):
name = models.CharField(max_length=100)
class Dog(Animal):
breed = models.CharField(max_length=100)
class Cat(Animal):
color = models.CharField(max_length=100)
  • Explanation: The Dog and Cat models will have their own tables but also include the fields from the Animal model.

5. Signals

Django signals allow you to execute code in response to certain events, like saving or deleting an object.

from django.db.models.signals import pre_save
from django.dispatch import receiver
class Blog(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# Signal handler to convert title to uppercase before saving
@receiver(pre_save, sender=Blog)
def convert_title_to_uppercase(sender, instance, **kwargs):
instance.title = instance.title.upper()
  • Explanation: This signal handler ensures that before any Blog instance is saved, its title is converted to uppercase.

6. Custom Save Method

You can override the save() method to add custom logic when saving a model.

class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
discount_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
def save(self, *args, **kwargs):
if self.discount_price is None:
self.discount_price = self.price * 0.9 # 10% discount
super().save(*args, **kwargs)
  • Explanation: The Product model’s save() method is overridden to automatically set the discount_price to 10% off the regular price if discount_price is not provided.

7. Property Fields (Dynamic Attributes)

You can define dynamic attributes using the @property decorator. This is useful when you need to calculate a value dynamically based on other model fields.

class Order(models.Model):
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
discount = models.DecimalField(max_digits=5, decimal_places=2)
@property
def final_amount(self):
return self.total_amount - self.discount
  • Explanation: The final_amount is a dynamic field calculated as total_amount minus discount without requiring a database column.

8. Indexing and Constraints

a. Adding Indexes to Models

Indexes help speed up database queries. Django allows you to add custom indexes to a model.

class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
indexes = [
models.Index(fields=['name']),
models.Index(fields=['price']),
]
  • Explanation: This will add database indexes for the name and price fields, improving query performance.

b. Unique Constraints

class Product(models.Model):
sku = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=100)
class Meta:
constraints = [
models.UniqueConstraint(fields=['sku'], name='unique_sku')
]
  • Explanation: The sku field is constrained to be unique, ensuring that each product has a unique SKU.