from django.db import models
from django.utils import timezone
class TimeStampedModel(models.Model):
"""Abstract base class with created/updated timestamps."""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class SoftDeleteModel(models.Model):
"""Abstract base class for soft delete functionality."""
deleted_at = models.DateTimeField(null=True, blank=True)
class Meta:
abstract = True
def delete(self, using=None, keep_parents=False):
"""Soft delete by setting deleted_at timestamp."""
self.deleted_at = timezone.now()
self.save()
def hard_delete(self):
"""Permanently delete from database."""
super().delete()
from django.db import models
from core.models import TimeStampedModel, SoftDeleteModel
class Post(TimeStampedModel, SoftDeleteModel):
"""Post model with timestamps and soft delete."""
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
def __str__(self):
return self.title
Abstract base classes let me define common fields and methods without creating database tables. I set abstract = True in Meta. Concrete models inheriting from the abstract class get all its fields and methods. This is perfect for timestamps, soft deletes, or common metadata fields. Unlike multi-table inheritance, this doesn't create joins or extra queries. For polymorphic behavior, I use Django Polymorphic library. Abstract models can't be instantiated directly and don't appear in migrations as separate tables. This keeps the schema clean and code DRY.