Django Extensions is a comprehensive collection of custom extensions that enhance the Django web framework with additional management commands, utilities, and developer tools.
—
Django Extensions provides abstract model classes that implement common functionality patterns including timestamps, activation status, and title/description/slug combinations. These base classes reduce boilerplate code and provide consistent implementations of frequently needed model patterns.
Abstract base class that provides self-managed "created" and "modified" fields with automatic timestamp handling.
class TimeStampedModel(models.Model):
created: CreationDateTimeField # Automatically set on creation
modified: ModificationDateTimeField # Automatically updated on save
def save(self, update_modified=True, **kwargs):
"""
Save the model instance.
Parameters:
- update_modified: If False, prevents modification timestamp update
"""
class Meta:
get_latest_by = 'modified'
abstract = TrueUsage examples:
from django.db import models
from django_extensions.db.models import TimeStampedModel
class Article(TimeStampedModel):
title = models.CharField(max_length=200)
content = models.TextField()
# created and modified fields are automatically added
class BlogPost(TimeStampedModel):
title = models.CharField(max_length=200)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
def save_without_timestamp_update(self, *args, **kwargs):
# Save without updating the modified timestamp
self.update_modified = False
self.save(*args, **kwargs)
# Usage
article = Article.objects.create(title="My Article", content="Content")
print(article.created) # DateTime when created
print(article.modified) # Same as created initially
article.content = "Updated content"
article.save()
print(article.modified) # Updated to current time
# Get latest article
latest = Article.objects.latest() # Uses 'modified' field by defaultAbstract base class that provides standard title and description fields.
class TitleDescriptionModel(models.Model):
title: CharField # max_length=255
description: TextField # blank=True, null=True
class Meta:
abstract = TrueUsage examples:
from django.db import models
from django_extensions.db.models import TitleDescriptionModel
class Category(TitleDescriptionModel):
# Inherits title and description fields
color = models.CharField(max_length=7) # For hex color codes
class Product(TitleDescriptionModel):
price = models.DecimalField(max_digits=10, decimal_places=2)
sku = models.CharField(max_length=50, unique=True)
# Usage
category = Category.objects.create(
title="Electronics",
description="Electronic devices and accessories",
color="#3498db"
)Abstract base class that extends TitleDescriptionModel with an auto-generated slug field.
class TitleSlugDescriptionModel(TitleDescriptionModel):
title: CharField # Inherited from TitleDescriptionModel
description: TextField # Inherited from TitleDescriptionModel
slug: AutoSlugField # Auto-populated from 'title'
# Custom slugify function can be defined on the model
def slugify_function(self, content):
"""
Optional: Define custom slugification logic.
Parameters:
- content: The content to be slugified
Returns:
- str: The slugified content
"""
class Meta:
abstract = TrueUsage examples:
from django.db import models
from django_extensions.db.models import TitleSlugDescriptionModel
class Page(TitleSlugDescriptionModel):
content = models.TextField()
is_published = models.BooleanField(default=False)
class CustomSlugPage(TitleSlugDescriptionModel):
content = models.TextField()
def slugify_function(self, content):
# Custom slugification: use underscores instead of hyphens
return content.replace(' ', '_').lower()
# Usage
page = Page.objects.create(
title="About Us",
description="Information about our company",
content="We are a great company..."
)
print(page.slug) # "about-us" (automatically generated)
# With custom slugify function
custom_page = CustomSlugPage.objects.create(
title="Contact Info",
content="Our contact information"
)
print(custom_page.slug) # "contact_info" (uses custom function)A complete system for managing model activation status with querysets and managers.
class ActivatorQuerySet(models.QuerySet):
def active(self):
"""Return queryset filtered to active instances only."""
def inactive(self):
"""Return queryset filtered to inactive instances only."""
class ActivatorModelManager(models.Manager):
def get_queryset(self):
"""Use ActivatorQuerySet for all queries."""
def active(self):
"""Return active instances: Model.objects.active()"""
def inactive(self):
"""Return inactive instances: Model.objects.inactive()"""
class ActivatorModel(models.Model):
INACTIVE_STATUS: int = 0
ACTIVE_STATUS: int = 1
STATUS_CHOICES: tuple = (
(INACTIVE_STATUS, 'Inactive'),
(ACTIVE_STATUS, 'Active')
)
status: IntegerField # choices=STATUS_CHOICES, default=ACTIVE_STATUS
activate_date: DateTimeField # blank=True, null=True
deactivate_date: DateTimeField # blank=True, null=True
objects: ActivatorModelManager
def save(self, *args, **kwargs):
"""Automatically set activate_date if not provided."""
class Meta:
ordering = ('status', '-activate_date')
abstract = TrueUsage examples:
from django.db import models
from django_extensions.db.models import ActivatorModel
from django.utils import timezone
class Campaign(ActivatorModel):
name = models.CharField(max_length=200)
budget = models.DecimalField(max_digits=10, decimal_places=2)
class Promotion(ActivatorModel):
title = models.CharField(max_length=200)
discount_percent = models.IntegerField()
# Usage - Creating instances
campaign = Campaign.objects.create(
name="Summer Sale",
budget=5000.00,
# status defaults to ACTIVE_STATUS
# activate_date is automatically set to now()
)
# Deactivating a campaign
campaign.status = Campaign.INACTIVE_STATUS
campaign.deactivate_date = timezone.now()
campaign.save()
# Querying active/inactive instances
active_campaigns = Campaign.objects.active()
inactive_campaigns = Campaign.objects.inactive()
# Using in templates or serializers
for campaign in Campaign.objects.active():
print(f"Active: {campaign.name}")
# Scheduled activation
future_campaign = Campaign.objects.create(
name="Holiday Sale",
budget=10000.00,
activate_date=timezone.now() + timezone.timedelta(days=30)
)Base classes can be combined to create models with multiple features:
from django.db import models
from django_extensions.db.models import TimeStampedModel, ActivatorModel
# Multiple inheritance
class ManagedContent(TimeStampedModel, ActivatorModel):
title = models.CharField(max_length=200)
content = models.TextField()
class Meta:
# Inherit ordering from ActivatorModel, add created
ordering = ('status', '-activate_date', '-created')
# Custom combination with TitleSlugDescriptionModel
class PublishableArticle(TitleSlugDescriptionModel, TimeStampedModel, ActivatorModel):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
featured_image = models.ImageField(upload_to='articles/', blank=True)
def is_published(self):
return self.status == self.ACTIVE_STATUS
@classmethod
def published(cls):
return cls.objects.active()
# Usage
article = PublishableArticle.objects.create(
title="Django Best Practices",
description="A guide to Django development",
author=request.user
)
# Has: title, slug, description, created, modified, status, activate_date, deactivate_date
published_articles = PublishableArticle.published()# Pattern 1: Timestamped content with soft delete
class SoftDeleteTimeStampedModel(TimeStampedModel):
is_deleted = models.BooleanField(default=False)
deleted_at = models.DateTimeField(null=True, blank=True)
def delete(self, using=None, keep_parents=False):
# Soft delete instead of actual deletion
self.is_deleted = True
self.deleted_at = timezone.now()
self.save()
class Meta:
abstract = True
# Pattern 2: Hierarchical categories with timestamps
class Category(TitleSlugDescriptionModel, TimeStampedModel):
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order', 'title']
verbose_name_plural = 'categories'
# Pattern 3: User-owned content with activation
class UserContent(TitleDescriptionModel, ActivatorModel, TimeStampedModel):
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
is_public = models.BooleanField(default=False)
def can_view(self, user):
if self.status != self.ACTIVE_STATUS:
return False
return self.is_public or self.owner == user or user.is_staff
class Meta:
abstract = True# Extending ActivatorModel with custom methods
class PublishedQuerySet(ActivatorQuerySet):
def published(self):
return self.active().filter(publish_date__lte=timezone.now())
def featured(self):
return self.published().filter(is_featured=True)
class PublishedManager(ActivatorModelManager):
def get_queryset(self):
return PublishedQuerySet(self.model, using=self._db)
def published(self):
return self.get_queryset().published()
def featured(self):
return self.get_queryset().featured()
class Article(TitleSlugDescriptionModel, ActivatorModel, TimeStampedModel):
content = models.TextField()
publish_date = models.DateTimeField(default=timezone.now)
is_featured = models.BooleanField(default=False)
objects = PublishedManager()
class Meta:
ordering = ['-publish_date']
# Usage
Article.objects.published() # Active articles with publish_date <= now
Article.objects.featured() # Published + featured articlesInstall with Tessl CLI
npx tessl i tessl/pypi-django-extensions