Operator Error

adventures in software development and continuous learning

Create a Custom Authentication Decorator in Django

Django leverages Python’s ability to use decorators extensively. Decorators in Python are defined as:

A Python decorator is a specific change to the Python syntax that allows us to more conveniently alter functions and methods.

In layman’s terms, decorators allow us to dynamically alter functions without having to actually change the function itself. Sounds pretty neat? Well, you can read more about Python Decorators here and here.

The most common use of a decorator is the infamous login_required. This decorator is used in conjunction with a view that restricts access to authenticated users only.

1
2
3
4
5
6
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def my_view(request):
    return render(request, 'template.html')

This decorator is very useful, because I don’t have to actually change anything about my view to restrict access to it. However, what if you want to restrict a view to only authenticated users who are currently active? This can easily be accomplished with a custom decorator:

decorator.pyView Gist
1
2
3
4
5
6
7
8
from django.contrib.auth.decorators import user_passes_test, login_required

active_required = user_passes_test(lambda u: u.is_active,
                                   login_url='/profile/not_active')

def active_and_login_required(view_func):
    decorated_view_func = login_required(active_required(view_func))
    return decorated_view_func

Thanks to the user_passes_test decorator that comes with Django, we can chain this together with the login_required to create our custom decorator.

To break it down further, we pass an anonymous function into the user_passes_test decorator that returns the value of is_active, which is a boolean that designates if the user is active. Next we set the login_url parameter, which will redirect to this URL if the user is not active. For further reference, check out the actual source for user_passes_test.

Now we just update our view to incorporate our new, reusable decorator.py package and decorator.

1
2
3
4
5
6
7
from django.shortcuts import render

from myapp.decorators import active_and_login_required

@active_and_login_required
def my_view(request):
    return render(request, 'template.html')

Comments