Reusable, generic mixins for Django class-based views
—
Comprehensive authentication and authorization mixins for Django class-based views. These mixins provide fine-grained access control, from simple login requirements to complex permission checking and secure connection enforcement.
All access control mixins inherit common functionality for login redirects and permission handling. These attributes are available on all access mixins:
login_url = None - URL to redirect to for login (defaults to settings.LOGIN_URL)raise_exception = False - Whether to raise PermissionDenied or redirectredirect_field_name = 'next' - Field name for storing redirect URLredirect_unauthenticated_users = False - Special handling for unauthenticated usersCommon methods available on access mixins:
get_login_url() - Get the login URL (can be overridden)get_redirect_field_name() - Get redirect field name (can be overridden)handle_no_permission(request) - Handle permission failuresno_permissions_fail(request) - Default failure handler (redirects to login)Require user authentication with flexible redirect handling.
class LoginRequiredMixin(AccessMixin):
"""Requires the user to be authenticated"""
def dispatch(self, request, *args, **kwargs):
"""Call appropriate method after checking authentication"""Usage example:
from django.views.generic import ListView
from braces.views import LoginRequiredMixin
class ProtectedListView(LoginRequiredMixin, ListView):
model = MyModel
login_url = '/accounts/login/'
redirect_field_name = 'next'
raise_exception = False # Redirect instead of 403Require user to be unauthenticated, useful for login/register pages.
class AnonymousRequiredMixin(AccessMixin):
"""Requires the user to be unauthenticated"""
authenticated_redirect_url = '/accounts/profile/'
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler after guaranteeing anonymity"""
def get_authenticated_redirect_url(self):
"""Return the reversed authenticated redirect url"""Usage example:
from django.views.generic import TemplateView
from braces.views import AnonymousRequiredMixin
class LoginPageView(AnonymousRequiredMixin, TemplateView):
template_name = 'registration/login.html'
authenticated_redirect_url = '/dashboard/'Require specific Django permissions before granting access.
class PermissionRequiredMixin(AccessMixin):
"""Requires user to have specific permission(s)"""
permission_required = None
object_level_permissions = False
def get_permission_required(self, request=None):
"""Get required permissions and return them"""
def check_permissions(self, request):
"""Returns whether user has permissions"""
def dispatch(self, request, *args, **kwargs):
"""Check user has required permission"""Usage example:
from django.views.generic import UpdateView
from braces.views import PermissionRequiredMixin
class EditPostView(PermissionRequiredMixin, UpdateView):
model = Post
permission_required = 'blog.change_post'
object_level_permissions = True # Check permission on specific object
raise_exception = True # Return 403 instead of redirectComplex permission checking with AND/OR logic for multiple permissions.
class MultiplePermissionsRequiredMixin(PermissionRequiredMixin):
"""Allows specifying multiple permissions with 'all' and 'any' logic"""
permissions = None # Dict with 'all' and/or 'any' keys
def get_permission_required(self, request=None):
"""Get which permission is required"""
def check_permissions(self, request):
"""Get the permissions, both all and any"""Usage example:
from django.views.generic import CreateView
from braces.views import MultiplePermissionsRequiredMixin
class CreatePostView(MultiplePermissionsRequiredMixin, CreateView):
model = Post
permissions = {
'all': ('blog.add_post', 'blog.change_post'), # Must have ALL of these
'any': ('blog.publish_post', 'user.is_editor') # Must have ANY of these
}Require membership in specific Django groups.
class GroupRequiredMixin(AccessMixin):
"""Requires user membership in specific group(s)"""
group_required = None # String, list, or tuple of group names
def get_group_required(self):
"""Get which group's membership is required"""
def check_membership(self, groups):
"""Check user's membership in required groups"""
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler if user is group member"""Usage example:
from django.views.generic import ListView
from braces.views import GroupRequiredMixin
class AdminListView(GroupRequiredMixin, ListView):
model = MyModel
group_required = ['admins', 'moderators'] # User must be in one of these groupsCustom validation logic for user access control.
class UserPassesTestMixin(AccessMixin):
"""User must pass custom test before accessing view"""
def test_func(self, user):
"""The function to test the user with - must be implemented"""
def get_test_func(self):
"""Get the test function"""
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler if user passes test"""Usage example:
from django.views.generic import DetailView
from braces.views import UserPassesTestMixin
class ProfileView(UserPassesTestMixin, DetailView):
model = Profile
def test_func(self, user):
profile = self.get_object()
return user == profile.user or user.is_staffQuick access control for administrative users.
class SuperuserRequiredMixin(AccessMixin):
"""Require users to be superusers"""
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler if user is superuser"""
class StaffuserRequiredMixin(AccessMixin):
"""Require users to be marked as staff"""
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler if user is staff member"""Usage example:
from django.views.generic import ListView
from braces.views import SuperuserRequiredMixin
class AdminOnlyView(SuperuserRequiredMixin, ListView):
model = SensitiveModel
raise_exception = TrueEnforce HTTPS connections with automatic redirect or error handling.
class SSLRequiredMixin:
"""Require requests to be made over secure connection"""
raise_exception = False
def dispatch(self, request, *args, **kwargs):
"""Call appropriate handler if connection is secure"""Usage example:
from django.views.generic import FormView
from braces.views import SSLRequiredMixin
class PaymentFormView(SSLRequiredMixin, FormView):
template_name = 'payment.html'
raise_exception = True # Return 404 for non-HTTPS requestsForce re-authentication after a specified time period.
class RecentLoginRequiredMixin(LoginRequiredMixin):
"""Require user to have logged in within number of seconds"""
max_last_login_delta = 1800 # Default 30 minutes
def dispatch(self, request, *args, **kwargs):
"""Call appropriate method if user's login is recent"""Usage example:
from django.views.generic import UpdateView
from braces.views import RecentLoginRequiredMixin
class SensitiveUpdateView(RecentLoginRequiredMixin, UpdateView):
model = UserProfile
max_last_login_delta = 900 # Require login within last 15 minutesMultiple access mixins can be combined for layered security:
from braces.views import LoginRequiredMixin, PermissionRequiredMixin, SSLRequiredMixin
class SecureAdminView(SSLRequiredMixin, LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = SensitiveModel
permission_required = 'myapp.change_sensitivemodel'
raise_exception = TrueAccess mixins should be placed leftmost in the inheritance chain (except when combined with CsrfExemptMixin, which should be leftmost).
Install with Tessl CLI
npx tessl i tessl/pypi-django-braces