Reusable, generic mixins for Django class-based views
—
Utility mixins for HTTP header manipulation, cache control, context enhancement, and URL handling. These mixins provide essential web development utilities for Django class-based views.
Set custom HTTP headers on responses with flexible configuration.
class HeaderMixin:
"""Add extra HTTP headers to response"""
headers = {}
def get_headers(self, request):
"""Override to customize headers dynamically"""
def dispatch(self, request, *args, **kwargs):
"""Override to customize header retrieval"""Usage example:
from django.views.generic import TemplateView
from braces.views import HeaderMixin
class CustomHeaderView(HeaderMixin, TemplateView):
template_name = 'page.html'
headers = {
'X-Custom-Header': 'MyValue',
'Access-Control-Allow-Origin': '*',
'X-Frame-Options': 'DENY'
}
def get_headers(self, request):
headers = super().get_headers(request)
# Dynamic headers based on request
if request.user.is_authenticated:
headers['X-User-ID'] = str(request.user.id)
# Conditional CORS
if request.META.get('HTTP_ORIGIN') in self.allowed_origins:
headers['Access-Control-Allow-Origin'] = request.META['HTTP_ORIGIN']
return headersFine-grained cache control with support for all cache-control directives.
class CacheControlMixin:
"""Mixin for setting Cache-Control options"""
cachecontrol_public = None
cachecontrol_private = None
cachecontrol_no_cache = None
cachecontrol_no_transform = None
cachecontrol_must_revalidate = None
cachecontrol_proxy_revalidate = None
cachecontrol_max_age = None
cachecontrol_s_maxage = None
@classmethod
def get_cachecontrol_options(cls):
"""Compile dictionary of selected cache options"""
@classmethod
def as_view(cls, *args, **kwargs):
"""Wrap view with appropriate cache controls"""Usage example:
from django.views.generic import ListView
from braces.views import CacheControlMixin
class CachedListView(CacheControlMixin, ListView):
model = MyModel
# Cache for 1 hour, allow public caching
cachecontrol_max_age = 3600
cachecontrol_public = True
cachecontrol_must_revalidate = True
class PrivateDataView(CacheControlMixin, ListView):
model = UserData
# Private data, no caching
cachecontrol_private = True
cachecontrol_no_cache = True
cachecontrol_no_transform = TruePrevent all upstream caching with Django's never_cache decorator.
class NeverCacheMixin:
"""Applies never_cache decorator to prevent HTTP-based caching"""
@classmethod
def as_view(cls, *args, **kwargs):
"""Wrap view with never_cache decorator"""Usage example:
from django.views.generic import FormView
from braces.views import NeverCacheMixin
class SensitiveFormView(NeverCacheMixin, FormView):
template_name = 'sensitive_form.html'
# This view will never be cached by browsers or proxiesAdd static context data and headlines to template context.
class StaticContextMixin:
"""Set static context items via view attribute"""
static_context = None
def get_context_data(self, **kwargs):
"""Update context to include static content"""
def get_static_context(self):
"""Fetch static content from view"""
class SetHeadlineMixin:
"""Define headline context item as view attribute"""
headline = None
def get_context_data(self, **kwargs):
"""Add headline to context"""
def get_headline(self):
"""Fetch headline from instance"""Usage example:
from django.views.generic import ListView
from braces.views import StaticContextMixin, SetHeadlineMixin
class EnhancedListView(SetHeadlineMixin, StaticContextMixin, ListView):
model = MyModel
headline = "My Items List"
static_context = {
'page_title': 'Items',
'show_filters': True,
'max_items_per_page': 50,
'api_endpoints': {
'create': '/api/items/',
'bulk_delete': '/api/items/bulk-delete/'
}
}
def get_headline(self):
# Dynamic headlines
count = self.get_queryset().count()
return f"My Items List ({count} items)"
def get_static_context(self):
context = super().get_static_context()
# Dynamic static context
if self.request.user.is_staff:
context['admin_tools'] = True
context['show_advanced_filters'] = True
return contextEnforce canonical URLs with automatic redirects for slug-based detail views.
class CanonicalSlugDetailMixin:
"""Enforce canonical slug in URL"""
def dispatch(self, request, *args, **kwargs):
"""Redirect to appropriate URL if necessary"""
def get_canonical_slug(self):
"""Provide method to return correct slug for object"""Usage example:
from django.views.generic import DetailView
from braces.views import CanonicalSlugDetailMixin
class ArticleDetailView(CanonicalSlugDetailMixin, DetailView):
model = Article
slug_field = 'slug'
slug_url_kwarg = 'slug'
def get_canonical_slug(self):
# Use the object's current slug as canonical
return self.get_object().slug
# If Article model has get_canonical_slug method:
class SmartArticleDetailView(CanonicalSlugDetailMixin, DetailView):
model = Article
# Will automatically use article.get_canonical_slug()Model implementation:
from django.db import models
from django.utils.text import slugify
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
def get_canonical_slug(self):
# Always generate slug from current title
return slugify(self.title)
def save(self, *args, **kwargs):
# Update slug on save
self.slug = self.get_canonical_slug()
super().save(*args, **kwargs)Route all HTTP methods to a single handler method.
class AllVerbsMixin:
"""Call single method for all HTTP verbs"""
all_handler = 'all' # Method name to handle all requests
def dispatch(self, request, *args, **kwargs):
"""Call the all handler"""Usage example:
from django.views.generic import View
from braces.views import AllVerbsMixin
class UniversalAPIView(AllVerbsMixin, View):
all_handler = 'handle_request'
def handle_request(self, request, *args, **kwargs):
# Handle all HTTP methods (GET, POST, PUT, DELETE, etc.)
method = request.method
if method == 'GET':
return self.handle_get(request, *args, **kwargs)
elif method == 'POST':
return self.handle_post(request, *args, **kwargs)
# ... handle other methods
return HttpResponse(f"Method {method} handled", status=200)Standard headers for API endpoints:
from django.views.generic import View
from braces.views import HeaderMixin, NeverCacheMixin, CsrfExemptMixin
class APIView(HeaderMixin, NeverCacheMixin, CsrfExemptMixin, View):
headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'X-API-Version': '1.0'
}
def get_headers(self, request):
headers = super().get_headers(request)
# CORS headers based on request
origin = request.META.get('HTTP_ORIGIN')
if origin in settings.ALLOWED_ORIGINS:
headers['Access-Control-Allow-Origin'] = origin
headers['Access-Control-Allow-Credentials'] = 'true'
return headersOptimize content delivery with appropriate caching:
from django.views.generic import DetailView
from braces.views import CacheControlMixin, HeaderMixin
class OptimizedContentView(CacheControlMixin, HeaderMixin, DetailView):
model = Article
# Cache publicly for 1 hour
cachecontrol_public = True
cachecontrol_max_age = 3600
headers = {
'Vary': 'Accept-Encoding',
'X-Content-Type-Options': 'nosniff'
}
def get_cachecontrol_options(self):
options = super().get_cachecontrol_options()
# Dynamic cache control
obj = self.get_object()
if obj.is_breaking_news:
options['max_age'] = 300 # 5 minutes for breaking news
return optionsRich context for complex templates:
from django.views.generic import ListView
from braces.views import StaticContextMixin, SetHeadlineMixin
class DashboardView(SetHeadlineMixin, StaticContextMixin, ListView):
model = UserAction
template_name = 'dashboard.html'
def get_headline(self):
return f"Welcome back, {self.request.user.get_full_name()}"
def get_static_context(self):
return {
'navigation_items': self.get_navigation(),
'user_permissions': list(self.request.user.get_all_permissions()),
'feature_flags': settings.FEATURE_FLAGS,
'current_version': settings.APP_VERSION
}
def get_navigation(self):
# Build navigation based on user permissions
nav = []
if self.request.user.has_perm('app.view_reports'):
nav.append({'label': 'Reports', 'url': '/reports/'})
return navInstall with Tessl CLI
npx tessl i tessl/pypi-django-braces