Skip to main content Brad's PyNotes

Django Authentication and Permissions

TL;DR

Django provides a complete authentication and authorization sub-system out of the box. Use @login_required to restrict views to authenticated users and @permission_required to enforce granular access control based on custom permissions defined in your models.

Interesting!

Django automatically creates four default permissions for every model (add, change, delete, view) during migrations, but you can define custom permissions in your model’s Meta class to implement fine-grained authorization for any business logic you need.

The Authentication Foundation

Django’s authentication system lives in django.contrib.auth and works seamlessly with sessions via middleware. The @login_required decorator enforces authentication checks:

python code snippet start

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    # Only authenticated users can access this view
    return render(request, 'template.html')

python code snippet end

When an unauthenticated user tries to access a protected view, Django redirects them to the login page (defined by settings.LOGIN_URL) and stores the original URL in a “next” query parameter so they can be redirected back after login.

Custom Permissions in Models

You can define custom permissions in your model’s Meta class. These permissions use the format <app_label>.<permission_codename>:

python code snippet start

from django.db import models

class Shape(models.Model):
    color = models.CharField(max_length=10)
    shape_type = models.CharField(max_length=10)

    class Meta:
        permissions = [
            ("view_shapes_canvas", "Can view the shapes canvas"),
            ("add_red_shape", "Can add red shapes"),
            ("add_green_shape", "Can add green shapes"),
            ("add_blue_shape", "Can add blue shapes"),
        ]

python code snippet end

These custom permissions appear in the Django admin interface and can be assigned to individual users or groups.

The @permission_required Decorator

The @permission_required decorator enforces authorization checks before allowing view access:

python code snippet start

from django.contrib.auth.decorators import permission_required

@login_required
@permission_required('shapes.view_shapes_canvas', raise_exception=True)
def canvas_view(request):
    # Only users with 'view_shapes_canvas' permission can access
    return render(request, 'canvas.html')

python code snippet end

Setting raise_exception=True returns a 403 Forbidden response instead of redirecting to the login page. You can also pass multiple permissions as a list when a view requires several permissions.

Checking Permissions Programmatically

Use request.user.has_perm() to check permissions within your views:

python code snippet start

@login_required
def add_shape(request):
    color = request.POST.get('color')

    # Check permission based on color
    if color == 'red' and not request.user.has_perm('shapes.add_red_shape'):
        messages.error(request, "You don't have permission to add red shapes!")
        return redirect('canvas')

    # Permission granted, create the shape
    Shape.objects.create(color=color, ...)

python code snippet end

This pattern lets you implement authorization logic beyond simple built-in object-based checks.

Dynamic Permission-Based UI

Pass permission checks to your templates to enable or disable UI elements:

python code snippet start

context = {
    'can_add_red': request.user.has_perm('shapes.add_red_shape'),
    'can_add_green': request.user.has_perm('shapes.add_green_shape'),
    'can_delete': request.user.has_perm('shapes.delete_shape'),
}
return render(request, 'canvas.html', context)

python code snippet end

In your template:

html code snippet start

<button {% if not can_add_red %}disabled{% endif %}>
    Add Red Shape
</button>

html code snippet end

Automatic Permission Creation

Django automatically creates four permissions for each model during migrations: add_<modelname>, change_<modelname>, delete_<modelname>, and view_<modelname>. These are available immediately after running python manage.py migrate, assuming django.contrib.auth is in your INSTALLED_APPS.

Summary

This demonstrates how Django’s permission system enables fine-grained access control for any application logic, not just CRUD operations.

Django’s built-in authentication and permissions system provides everything you need to implement secure, granular authorization without reaching for third-party packages. The combination of decorators, programmatic checks, and custom permissions makes it straightforward to build applications with complex access control requirements.

Django’s decorator-based authentication builds on Python’s decorator patterns from functools . The permission system integrates naturally with Python classes through model definitions. Understanding function annotations helps when working with Django’s type-aware tools and modern API frameworks.

Reference: Django Authentication System