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 enhanced Django admin functionality including custom filters and widgets for improved administrative interfaces. These extensions integrate seamlessly with Django's admin system to provide better user experience and additional functionality.
Custom filter classes that enhance Django admin list views with specialized filtering options.
class NullFieldListFilter(admin.SimpleListFilter):
title: str = 'has value'
parameter_name: str = 'has_value'
def __init__(self, request, params, model, model_admin):
"""
Admin filter for handling null/non-null field filtering.
Provides filtering options for fields that can be null:
- "Has value" (field is not null)
- "No value" (field is null)
Usage in ModelAdmin:
list_filter = [('field_name', NullFieldListFilter)]
"""
def lookups(self, request, model_admin):
"""
Return filter options.
Returns:
- tuple: (('1', 'Has value'), ('0', 'No value'))
"""
def queryset(self, request, queryset):
"""
Filter the queryset based on selection.
Parameters:
- request: HttpRequest object
- queryset: QuerySet to filter
Returns:
- QuerySet: Filtered queryset
"""Usage examples:
from django.contrib import admin
from django_extensions.admin import NullFieldListFilter
from myapp.models import Article
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'published_date', 'featured_image']
# Filter by fields that can be null
list_filter = [
('published_date', NullFieldListFilter),
('featured_image', NullFieldListFilter),
('author', NullFieldListFilter),
]
# Standard filters can be mixed with custom filters
list_filter = [
'category', # Regular field filter
('published_date', NullFieldListFilter), # Custom null filter
'is_featured', # Boolean field filter
]
# Custom implementation for specific fields
class CustomNullFilter(NullFieldListFilter):
title = 'has thumbnail'
parameter_name = 'has_thumbnail'
@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
list_filter = [('thumbnail', CustomNullFilter)]Complete system for ForeignKey autocomplete functionality including admin mixins, ModelAdmin classes, and inline classes.
class ForeignKeyAutocompleteAdminMixin:
related_search_fields: dict[str, tuple[str, ...]] # Field mapping to search fields
related_string_functions: dict[str, Callable] # Custom string representations
autocomplete_limit: int | None # Limit autocomplete results
def __init__(self):
"""
Admin mixin for models using the autocomplete feature.
Configuration:
- related_search_fields: Maps field names to searchable target fields
- related_string_functions: Custom string representation functions
- autocomplete_limit: Maximum number of results (default from settings)
"""
def foreignkey_autocomplete(self, request):
"""
AJAX endpoint for autocomplete search functionality.
Parameters:
- request: HttpRequest with query parameters (q, app_label, model_name, etc.)
Returns:
- HttpResponse: Text response with autocomplete results
"""
def get_related_filter(self, model, request):
"""
Hook for additional filtering in autocomplete queries.
Parameters:
- model: Model class being searched
- request: Current HttpRequest
Returns:
- Q object or None: Additional filter for queryset
"""
def get_help_text(self, field_name, model_name):
"""Generate help text for autocomplete fields."""
class ForeignKeyAutocompleteAdmin(ForeignKeyAutocompleteAdminMixin, admin.ModelAdmin):
"""ModelAdmin with ForeignKey autocomplete functionality."""
class ForeignKeyAutocompleteTabularInline(ForeignKeyAutocompleteAdminMixin, admin.TabularInline):
"""TabularInline with ForeignKey autocomplete functionality."""
class ForeignKeyAutocompleteStackedInline(ForeignKeyAutocompleteAdminMixin, admin.StackedInline):
"""StackedInline with ForeignKey autocomplete functionality."""Usage examples:
from django.contrib import admin
from django_extensions.admin import ForeignKeyAutocompleteAdmin
from myapp.models import Article, Author, Category
class ArticleAdmin(ForeignKeyAutocompleteAdmin):
# Configure autocomplete for specific fields
related_search_fields = {
'author': ('first_name', 'last_name', 'email'),
'category': ('name', 'description'),
}
# Optional: Custom string representations
related_string_functions = {
'author': lambda obj: f"{obj.first_name} {obj.last_name} ({obj.email})",
}
# Optional: Limit autocomplete results
autocomplete_limit = 20
admin.site.register(Article, ArticleAdmin)
# Using with inlines
class CommentInline(ForeignKeyAutocompleteTabularInline):
model = Comment
related_search_fields = {
'author': ('username', 'email'),
}
class ArticleWithCommentsAdmin(admin.ModelAdmin):
inlines = [CommentInline]Enhanced widgets for Django admin forms that improve user experience and provide additional functionality.
class ForeignKeySearchInput(widgets.Input):
input_type: str = 'text'
template_name: str = 'django_extensions/widgets/foreignkey_searchinput.html'
def __init__(self, rel, admin_site, attrs=None, using=None):
"""
Widget for displaying ForeignKeys in autocomplete search input.
Provides enhanced foreign key selection with search functionality
instead of dropdown menus for better performance with large datasets.
Parameters:
- rel: ForeignKey relationship
- admin_site: AdminSite instance
- attrs: HTML attributes for the widget
- using: Database alias to use
"""
def format_value(self, value):
"""
Format the value for display in the input field.
Parameters:
- value: The foreign key value
Returns:
- str: Formatted value for display
"""
def build_attrs(self, base_attrs, extra_attrs=None):
"""
Build HTML attributes for the widget.
Returns:
- dict: HTML attributes dictionary
"""Usage examples:
from django.contrib import admin
from django.db import models
from django_extensions.admin import ForeignKeySearchInput
class Article(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
class Category(models.Model):
name = models.CharField(max_length=100)
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
# Use search input for foreign key fields
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name in ['author', 'category']:
kwargs['widget'] = ForeignKeySearchInput(
db_field.remote_field,
admin.site
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
# Alternative: Custom form with search widgets
from django import forms
class ArticleAdminForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
widgets = {
'author': ForeignKeySearchInput(
Article._meta.get_field('author').remote_field,
admin.site
),
'category': ForeignKeySearchInput(
Article._meta.get_field('category').remote_field,
admin.site
),
}
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
form = ArticleAdminFormThe admin extensions include custom templates that integrate with Django's admin interface:
<!-- django_extensions/widgets/foreignkey_searchinput.html -->
<input type="{{ widget.type }}"
name="{{ widget.name }}"
value="{{ widget.value|default:'' }}"
{% if widget.attrs.id %} id="{{ widget.attrs.id }}"{% endif %}
{% for name, value in widget.attrs.items %}
{% if value is not False %} {{ name }}{% if value is not True %}="{{ value }}"{% endif %}{% endif %}
{% endfor %}>
<!-- Include JavaScript for search functionality -->
<script type="text/javascript">
// Enhanced search and autocomplete functionality
// Integrates with Django admin's existing JavaScript
</script>from django.contrib import admin
from django.db import models
from django_extensions.admin import NullFieldListFilter, ForeignKeySearchInput
class BaseAdmin(admin.ModelAdmin):
"""Base admin class with common django-extensions functionality."""
def formfield_for_foreignkey(self, db_field, request, **kwargs):
# Use search input for all foreign key fields
kwargs['widget'] = ForeignKeySearchInput(
db_field.remote_field,
admin.site
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
class TimestampedAdmin(BaseAdmin):
"""Admin for models with timestamp fields."""
readonly_fields = ['created', 'modified']
list_display = ['__str__', 'created', 'modified']
list_filter = [
('created', admin.DateFieldListFilter),
('modified', admin.DateFieldListFilter),
]
class PublishableAdmin(BaseAdmin):
"""Admin for models with publication status."""
list_display = ['title', 'is_published', 'publish_date']
list_filter = [
'is_published',
('publish_date', NullFieldListFilter),
('publish_date', admin.DateFieldListFilter),
]
actions = ['mark_published', 'mark_unpublished']
def mark_published(self, request, queryset):
queryset.update(is_published=True)
mark_published.short_description = "Mark selected items as published"
def mark_unpublished(self, request, queryset):
queryset.update(is_published=False)
mark_unpublished.short_description = "Mark selected items as unpublished"
# Usage with django-extensions model base classes
from django_extensions.db.models import TimeStampedModel, TitleSlugDescriptionModel
class BlogPost(TimeStampedModel, TitleSlugDescriptionModel):
content = models.TextField()
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.SET_NULL)
is_published = models.BooleanField(default=False)
publish_date = models.DateTimeField(null=True, blank=True)
@admin.register(BlogPost)
class BlogPostAdmin(TimestampedAdmin, PublishableAdmin):
list_display = ['title', 'author', 'category', 'is_published', 'created']
list_filter = [
'is_published',
('author', NullFieldListFilter),
('category', NullFieldListFilter),
('publish_date', NullFieldListFilter),
('created', admin.DateFieldListFilter),
]
search_fields = ['title', 'content', 'author__username']
prepopulated_fields = {'slug': ('title',)}from django.contrib import admin
from django_extensions.admin import NullFieldListFilter
class CustomDateRangeFilter(admin.SimpleListFilter):
"""Custom filter extending NullFieldListFilter pattern."""
title = 'date range'
parameter_name = 'date_range'
def lookups(self, request, model_admin):
return (
('today', 'Today'),
('week', 'This Week'),
('month', 'This Month'),
('year', 'This Year'),
('no_date', 'No Date'),
)
def queryset(self, request, queryset):
from django.utils import timezone
from datetime import timedelta
if self.value() == 'today':
return queryset.filter(created__date=timezone.now().date())
elif self.value() == 'week':
week_ago = timezone.now() - timedelta(days=7)
return queryset.filter(created__gte=week_ago)
elif self.value() == 'month':
month_ago = timezone.now() - timedelta(days=30)
return queryset.filter(created__gte=month_ago)
elif self.value() == 'year':
year_ago = timezone.now() - timedelta(days=365)
return queryset.filter(created__gte=year_ago)
elif self.value() == 'no_date':
return queryset.filter(created__isnull=True)
return queryset
# Combined filtering approach
class AdvancedAdmin(admin.ModelAdmin):
list_filter = [
('created', CustomDateRangeFilter),
('author', NullFieldListFilter),
'status',
]from django_extensions.admin import ForeignKeySearchInput
from django.contrib import admin
class CustomSearchInput(ForeignKeySearchInput):
"""Customized foreign key search input with additional features."""
template_name = 'myapp/widgets/custom_search_input.html'
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs)
attrs.update({
'class': 'custom-search-input',
'data-search-url': '/admin/api/search/',
'placeholder': 'Type to search...'
})
return attrs
class EnhancedAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name in ['author', 'category']:
kwargs['widget'] = CustomSearchInput(
db_field.remote_field,
admin.site,
attrs={'class': 'enhanced-search'}
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)# 1. Use null filters for optional relationships
class ArticleAdmin(admin.ModelAdmin):
list_filter = [
('featured_image', NullFieldListFilter), # Optional image
('publish_date', NullFieldListFilter), # Optional publish date
('category', NullFieldListFilter), # Optional category
]
# 2. Combine with search for large datasets
class UserAdmin(admin.ModelAdmin):
search_fields = ['username', 'email', 'first_name', 'last_name']
list_filter = [
('last_login', NullFieldListFilter),
('date_joined', admin.DateFieldListFilter),
]
# 3. Use search inputs for performance with large FK sets
class OrderAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
# Use search input for customer selection (many customers)
if db_field.name == 'customer':
kwargs['widget'] = ForeignKeySearchInput(
db_field.remote_field,
admin.site
)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
# 4. Custom admin with multiple extensions
class ComprehensiveAdmin(admin.ModelAdmin):
list_per_page = 50
list_max_show_all = 200
def get_list_filter(self, request):
# Dynamic filter assignment
filters = list(self.list_filter)
# Add null filters for nullable foreign keys
for field in self.model._meta.get_fields():
if (field.is_relation and
getattr(field, 'null', False) and
hasattr(field, 'remote_field')):
filters.append((field.name, NullFieldListFilter))
return filtersInstall with Tessl CLI
npx tessl i tessl/pypi-django-extensions