Advanced View Patterns
Learn advanced view patterns including mixins, decorators, and custom view behaviors for complex applications.
70 min•By Priygop Team•Last updated: Feb 2026
View Mixins and Decorators
Django provides powerful mixins and decorators that allow you to add functionality to views without duplicating code. These patterns help create reusable, maintainable view logic.
Custom Mixins and Decorators
Example
# Custom mixins for views
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect
from django.contrib import messages
class AuthorRequiredMixin:
"""Mixin to ensure only the author can access the view"""
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.author != request.user:
raise PermissionDenied("You don't have permission to access this resource.")
return super().dispatch(request, *args, **kwargs)
class StaffRequiredMixin:
"""Mixin to ensure only staff members can access the view"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(request, "Access denied. Staff privileges required.")
return redirect('home')
return super().dispatch(request, *args, **kwargs)
class CacheControlMixin:
"""Mixin to add cache control headers"""
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
response['Cache-Control'] = 'public, max-age=300' # 5 minutes
return response
# Custom decorators
def author_required(view_func):
"""Decorator to ensure only the author can access the view"""
def wrapper(request, *args, **kwargs):
from .models import Post
post = get_object_or_404(Post, pk=kwargs['pk'])
if post.author != request.user:
messages.error(request, "You don't have permission to edit this post.")
return redirect('post_detail', pk=post.pk)
return view_func(request, *args, **kwargs)
return wrapper
def rate_limit(limit=100, period=3600):
"""Decorator to implement rate limiting"""
def decorator(view_func):
def wrapper(request, *args, **kwargs):
from django.core.cache import cache
from django.http import HttpResponseTooManyRequests
# Create a unique key for this user/IP
key = f"rate_limit:{request.user.id if request.user.is_authenticated else request.META.get('REMOTE_ADDR')}"
# Check current count
current_count = cache.get(key, 0)
if current_count >= limit:
return HttpResponseTooManyRequests("Rate limit exceeded. Please try again later.")
# Increment count
cache.set(key, current_count + 1, period)
return view_func(request, *args, **kwargs)
return wrapper
return decorator
# Usage examples
class PostEditView(AuthorRequiredMixin, LoginRequiredMixin, UpdateView):
model = Post
form_class = PostForm
template_name = 'blog/post_form.html'
@login_required
@author_required
@rate_limit(limit=50, period=3600)
def edit_post(request, pk):
# This view is rate-limited and author-restricted
passPractice Exercise: View Factory Pattern
Example
# View Factory Pattern for Dynamic Views
from django.views.generic import ListView, DetailView
from django.db import models
class ViewFactory:
"""Factory class to create views dynamically"""
@staticmethod
def create_list_view(model, template_name=None, context_object_name=None, **kwargs):
"""Create a ListView for any model"""
if template_name is None:
template_name = f'{model._meta.app_label}/{model._meta.model_name}_list.html'
if context_object_name is None:
context_object_name = f'{model._meta.model_name}_list'
class DynamicListView(ListView):
model = model
template_name = template_name
context_object_name = context_object_name
def get_queryset(self):
"""Filter queryset based on request parameters"""
queryset = super().get_queryset()
# Apply filters from query parameters
for key, value in self.request.GET.items():
if hasattr(model, key) and value:
if isinstance(getattr(model, key), models.CharField):
queryset = queryset.filter(**{f'{key}__icontains': value})
else:
queryset = queryset.filter(**{key: value})
return queryset
def get_context_data(self, **kwargs):
"""Add dynamic context data"""
context = super().get_context_data(**kwargs)
# Add filter form context
context['filters'] = self.request.GET
context['model_name'] = model._meta.verbose_name_plural
return context
# Apply additional kwargs
for key, value in kwargs.items():
setattr(DynamicListView, key, value)
return DynamicListView
@staticmethod
def create_detail_view(model, template_name=None, context_object_name=None, **kwargs):
"""Create a DetailView for any model"""
if template_name is None:
template_name = f'{model._meta.app_label}/{model._meta.model_name}_detail.html'
if context_object_name is None:
context_object_name = model._meta.model_name
class DynamicDetailView(DetailView):
model = model
template_name = template_name
context_object_name = context_object_name
def get_context_data(self, **kwargs):
"""Add dynamic context data"""
context = super().get_context_data(**kwargs)
# Add related objects if they exist
obj = self.get_object()
# Look for related fields
for field in model._meta.get_fields():
if field.is_relation and not field.auto_created:
try:
if field.many_to_many:
related_objects = getattr(obj, field.name).all()
else:
related_objects = getattr(obj, field.name)
context[f'{field.name}_list'] = related_objects
except:
pass
return context
# Apply additional kwargs
for key, value in kwargs.items():
setattr(DynamicDetailView, key, value)
return DynamicDetailView
# Usage example
from .models import Post, Category, Tag
# Create views dynamically
PostListView = ViewFactory.create_list_view(
Post,
paginate_by=10,
ordering=['-created_date']
)
CategoryListView = ViewFactory.create_list_view(
Category,
template_name='blog/category_list.html'
)
TagDetailView = ViewFactory.create_detail_view(
Tag,
template_name='blog/tag_detail.html'
)
# Register in urls.py
# urlpatterns = [
# path('posts/', PostListView.as_view(), name='post_list'),
# path('categories/', CategoryListView.as_view(), name='category_list'),
# path('tag/<slug:slug>/', TagDetailView.as_view(), name='tag_detail'),
# ]Try It Yourself — Views & Templates
Try It Yourself — Views & TemplatesHTML
HTML Editor
✓ ValidTab = 2 spaces
HTML|32 lines|1680 chars|✓ Valid syntax
UTF-8