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 anAuthor
, meaning an author can have multiple books. Theon_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 multipleCourse
instances, and eachCourse
can have multipleStudent
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 oneProfile
, and aProfile
is tied to exactly oneUser
.
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, orBook.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
andEvent
will inheritcreated_at
andupdated_at
fromTimeStampedModel
.
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
andCat
models will have their own tables but also include the fields from theAnimal
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_savefrom 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, itstitle
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’ssave()
method is overridden to automatically set thediscount_price
to 10% off the regularprice
ifdiscount_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 astotal_amount
minusdiscount
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
andprice
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.