Class-based Views
Django Shield works with class-based views (CBVs) just like function-based views.
Basic Usage
Apply @guard to the class:
from django.views import View
from django.http import HttpResponse
from django_shield import guard
@guard('user.is_staff')
class AdminDashboard(View):
def get(self, request):
return HttpResponse("Admin Dashboard")
The decorator wraps the dispatch method, so all HTTP methods are protected.
How It Works
When you decorate a CBV:
- Django Shield wraps the
dispatchmethod - Before any request handling, it checks permissions
- If denied, raises
PermissionDenied - If allowed, continues to
get(),post(), etc.
Object-level Permissions
Using get_object()
Django Shield automatically uses your view's get_object() method:
from django.views.generic import DetailView
from django_shield import guard, rule
from .models import Post
@rule
def is_author(user, post):
return post.author == user
@guard('is_author')
class PostEditView(DetailView):
model = Post
def get_object(self):
return Post.objects.get(pk=self.kwargs['pk'])
def get(self, request, *args, **kwargs):
post = self.get_object()
# User has permission if they reach here
...
Django Shield:
- Calls
get_object()to get the post - Passes it to
is_author(user, post) - Allows or denies based on the result
With Expression Syntax
@guard('obj.author == user')
class PostEditView(DetailView):
model = Post
def get_object(self):
return Post.objects.get(pk=self.kwargs['pk'])
def get(self, request, *args, **kwargs):
...
Views Without Objects
For list views or views without a specific object:
from django.views.generic import ListView
from django_shield import guard
@guard('user.is_authenticated')
class PostListView(ListView):
model = Post
template_name = 'posts/list.html'
Rules receive None as the object:
@rule
def is_premium_user(user, obj=None):
# obj is None for list views
return user.subscription.is_active
Generic Views
DetailView
from django.views.generic import DetailView
from django_shield import guard
@guard('obj.author == user or obj.is_public')
class PostDetailView(DetailView):
model = Post
template_name = 'posts/detail.html'
UpdateView
from django.views.generic import UpdateView
from django_shield import guard
@guard('obj.author == user')
class PostUpdateView(UpdateView):
model = Post
fields = ['title', 'content']
template_name = 'posts/edit.html'
DeleteView
from django.views.generic import DeleteView
from django_shield import guard
@guard('obj.author == user or user.is_staff')
class PostDeleteView(DeleteView):
model = Post
success_url = '/posts/'
CreateView
Create views don't have an object yet:
from django.views.generic import CreateView
from django_shield import guard
@guard('user.is_authenticated')
class PostCreateView(CreateView):
model = Post
fields = ['title', 'content']
ListView
from django.views.generic import ListView
from django_shield import guard
@guard('user.is_staff')
class AllPostsView(ListView):
model = Post
template_name = 'posts/admin_list.html'
Combining Multiple Rules
guard.all()
All rules must pass:
@guard.all('is_authenticated', 'is_verified', 'is_active')
class SecureView(View):
def get(self, request):
...
With objects:
@guard.all('is_team_member', 'has_edit_permission')
class ProjectEditView(UpdateView):
model = Project
def get_object(self):
return Project.objects.get(pk=self.kwargs['pk'])
guard.any()
At least one rule must pass:
@guard.any('is_owner', 'is_admin', 'is_editor')
class ContentManageView(View):
def get(self, request):
...
Error Handling in get_object()
If get_object() raises an exception, Django Shield continues without an object:
@guard('is_admin') # Rule only checks user
class SafeView(DetailView):
model = Post
def get_object(self):
# If this fails, permission check runs with obj=None
raise Http404("Not found")
This is useful when you want to check user-level permissions even if the object doesn't exist.
Complete Example
from django.views.generic import DetailView, UpdateView, DeleteView, ListView
from django_shield import guard, rule
from .models import Post
# Define rules
@rule
def is_author(user, post):
return post.author == user
@rule
def can_view(user, post):
return post.is_published or post.author == user
# List all published posts - anyone can view
class PostListView(ListView):
model = Post
queryset = Post.objects.filter(is_published=True)
# View single post - must be published or owner
@guard('can_view')
class PostDetailView(DetailView):
model = Post
# Edit post - must be author
@guard('is_author')
class PostEditView(UpdateView):
model = Post
fields = ['title', 'content']
# Delete post - must be author or staff
@guard('obj.author == user or user.is_staff')
class PostDeleteView(DeleteView):
model = Post
success_url = '/posts/'
# Admin view - staff only
@guard('user.is_staff')
class PostAdminView(ListView):
model = Post
template_name = 'posts/admin.html'
Mixins vs Decorators
You might be used to Django's LoginRequiredMixin. Django Shield uses decorators instead of mixins for consistency between function-based and class-based views.
# Django's approach with mixin
class MyView(LoginRequiredMixin, View):
...
# Django Shield approach with decorator
@guard('user.is_authenticated')
class MyView(View):
...
Both approaches work. Use whichever fits your codebase.