Monday, November 18, 2024
Google search engine
HomeLanguagesCreating Custom Decorator in Django for different permissions

Creating Custom Decorator in Django for different permissions

Decorators are incredibly helpful tools that allow you to dynamically change the functionality of a function, method, or class without having to utilize subclasses directly or modify the function’s source code. The decorator’s login_required, require HTTP methods, require POST, and others are needed when working with Django views.

But there are also situations where we want to utilize our own, uniquely designed decorators that function exactly how we want them to. Making decorators can also be useful if you apply the same logic in several places and find that it is simple to remove and add the decorator after your changes are complete.

Create Custom Decorator in Django

Here, we’ll set up a mechanism for students to access the site. In order to prevent students, teachers, and principals from accessing the pages that are available to other users. These privileges will be managed by the custom decorators we will create, and we will return various results/responses on each page access so that we can see various ways you can redirect or return a response for not accessible page. 

Stepwise Implementation

Step 1: Create your virtual environment and Install Django.

python -m venv custom_decorator_venv

Step 2: Create a project named CustomDecorator 

django-admin startproject CustomDecorator

Step 3: Move to the CustomDecorator folder and, then create an app named user in the project 

cd CustomDecorator
python manage.py startapp user

File Structure

This is the final file structure after following all the steps.

 

Step 4: Add a user app to the INSTALLED_APPS.

Python3




INSTALLED_APPS = [
    'user.apps.UserConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',


Step 5: Set this model to be your default auth model in settings.py

Python3




AUTH_USER_MODEL = "user.UserAccount"


Step 6: We will create UserAccountManager as the manager for the UserAccount model. In the UserAccount model, there will be five functions: create user, create superuser, create teacher, create student, and create principal. If the student boolean is True, the user is a student; if the is teacher boolean is True, the user is a teacher; and the same is true for the principal.

users/models.py

Python3




from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 
 
class UserAccountManager(BaseUserManager):
    def create_user(self, email, username, password=None):
        if not email:
            raise ValueError("Email field is required !")
        if not username:
            raise ValueError("Username field is required !")
        if not password:
            raise ValueError("Password field is required !")
        user = self.model(
            email=email,
            username=username
        )
        user.set_password(password)
        user.save(using=self._db)
        return user
 
    def create_superuser(self, email, username, password):
        user = self.create_user(
            email=email, username=username, password=password)
        user.is_superuser = True
        user.is_staff = True
        user.is_admin = True
        user.save()
        return user
 
    def create_student(self, email, username, password):
        user = self.create_user(email, username, password)
        user.is_student = True
        user.save()
        return user
 
    def create_teacher(self, email, username, password):
        user = self.create_user(email, username, password)
        user.is_teacher = True
        user.save()
        return user
 
    def create_principal(self, email, username, password):
        user = self.create_user(email, username, password)
        user.is_principal = True
        user.save()
        return user
 
 
class UserAccount(AbstractBaseUser):
    username = models.CharField(max_length=200, blank=False, null=False)
    email = models.CharField(
        max_length=200, blank=False, null=False, unique=True)
 
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
 
    is_student = models.BooleanField(default=False)
    is_teacher = models.BooleanField(default=False)
    is_principal = models.BooleanField(default=False)
 
    objects = UserAccountManager()
 
    USERNAME_FIELD = "email"
 
    def __unicode__(self):
        return str(self.username)
 
    def has_perm(self, perm, obj=None):
        return self.is_admin
 
    def has_module_perms(self, app_label):
        return True


Step 7: Create a templates/user folder in the user app which will hold the following templates:

  • user/templates/user/homePage.html

This is for the home page of the site.  

HTML




<!DOCTYPE html>
 
<html>
    <body>
        {% if messages %}
            {% for message in messages   %}
 
                <div style = "color: red;">{{message}}</div>
 
            {% endfor %}
            <br>
            <br>
        {% endif %}
        {% if request.user.is_authenticated %}
        <h1> {{request.user.email}}</h1>
        <br>
 
            {% if request.user.is_student %}
 
                You are a student ...
 
            {% elif request.user.is_teacher %}
 
                You are a teacher ...
             
            {% elif  request.user.is_principal %}
 
                You are a principal ...
             
            {% else %}
 
                You don't have any privilege ...
 
            {% endif %}
             
        {% endif %}
 
        <br>
        <br>
        <a href = "{% url  'user_urls:login-user' %}">Login </a>
        <br>
        <br>
        <a href = "{% url  'user_urls:student-view' %}">Student View </a>
        <br>
        <br>
        <a href = "{% url  'user_urls:teacher-view' %}">Teacher View</a>
        <br>
        <br>
        <a href = "{% url  'user_urls:principal-view' %}">Principal View</a>
        <br>
        <br>
 
    </body>
</html>


  • user/templates/user/loginView.html

This is the home page telling which user you are and there are links for the other pages to the site,  we will create those links and views and it also displays the messages if there are some. 

HTML




{% if messages %}
 
    {% for message in messages %}
 
        <div style = "color: red ;">{{message}}</div>
        <br>
 
    {% endfor %}
 
{% endif %}
 
<form method = "post">
    {% csrf_token %}
{{form.as_p}}
<br>
<br>
<input type = "submit" value = "Login" />
</form>


  • user/templates/user/principalView.html

This is the view that will only be accessed by the user who is the principal.

HTML




<h1> {{request.user.email}}</h1>
<br>
<h1>
    You are a principal and you can access the page !
</h1>


  • user/templates/user/studentView.html

This is the student view and can be accessed by the student only. 

HTML




<h1> {{request.user.email}}</h1>
<br>
<h1>
    You are a student and you can access the page !
</h1>


  • user/templates/user/teacherView.html

This is the teacher’s view which can be only viewed by the teacher. 

HTML




<h1> {{request.user.email}}</h1>
<br>
 
<h1>
    You are a teacher and you can access the page !
</h1>


Step 8: user/decorators.py

Understanding the concept of decorators for this Project

Before creating our real decorator it is necessary to understand how to build a decorator, this is a pseudo code/skeleton of the decorator.

We have a function called test_function that accepts the necessary arguments depending on the circumstance and returns True or False. However, you can write your own function and define whatever statement you like. The decorator, either accepts or rejects the argument depending on the situation. The @wraps from the functool module take the function name that is to be decorated by the wrapper function.

The _wrapped_view(request,  *args, **kwargs) is the exact view in which the decorator will be wrapped, this is like a clone of the view around which the decorator will be wrapped, now you can perform any action like you can redirect, return any kind of response, or move on.

Python3




from functool import wraps
 
 
def test_function_works(*args, **kwargs):
      if args[0] == True:
      return True
    return False
 
def some_decorator(*decorator_args , **decorator_kwargs):
    def decorator(view_function):
        @wraps(view_function)
        def _wrapped_view(request, *view_args, **view_kwargs):
            print("The required actions will be taken here ! Well, \
            actually inside the _wrapped_view function")
             
            if not test_function_works(*arguments , **keyword_arguments):
                print("The necessary operation that will be taken if \
                        the test case fails !")
                 
            return view_function(request, *args, **kwargs)
        return _wrapped_view
    return decorator


Creating decorator

We have created three functions ( student_test_function, teacher_test_function, principal_test_function ) which return True if the user is the following user in each case. These test_functions are to be used by decorators. 

  • student_access_only decorator: It wraps a view, if the currently logged-in user is not a student then he is returned a HttpResponse, that you are not a student and you cannot access the page. 
  • teacher_access_only decorator: It requires the input view_to_return, to which the user will be redirected if they are not teachers (we will generate the URLs).
  • principal_access_only decorator: it takes a message which will be sent to a certain page telling that you are not a principal and you cannot access the page and then you will be redirected to the login page. 

Python3




from functools import wraps
from django.contrib import messages
from django.shortcuts import redirect
from django.http import HttpResponse
 
 
def student_test_function(user):
    if user.is_student:
        return True
    return False
 
 
def teacher_test_function(user):
    if user.is_teacher:
        return True
    return False
 
 
def principal_test_function(user):
    if user.is_principal:
        return True
    return False
 
 
def student_access_only():
    def decorator(view):
        @wraps(view)
        def _wrapped_view(request, *args, **kwargs):
            if not student_test_function(request.user):
                return HttpResponse("You are not a student and \
                        you are not allowed to access this page !")
            return view(request, *args, **kwargs)
        return _wrapped_view
    return decorator
 
 
def teacher_access_only(view_to_return="user_urls:home-page"):
    def decorator(view):
        @wraps(view)
        def _wrapped_view(request, *args, **kwargs):
            if not teacher_test_function(request.user):
                messages.error(request, "You cannot access \
                                the teachers  page !")
                return redirect(view_to_return)
            return view(request, *args, **kwargs)
        return _wrapped_view
    return decorator
 
 
def principal_access_only(message_to_deliver="Not allowed to \
            access the principal's page , login as principal !"):
    def decorator(view):
        @wraps(view)
        def _wrapped_view(request, *args, **kwargs):
            if not principal_test_function(request.user):
                messages.error(request, message_to_deliver)
                return redirect("user_urls:login-user")
            return view(request, *args, **kwargs)
        return _wrapped_view
    return decorator


Step 9: user/views.py

We have four views that just render out the templates.

Python3




from django.shortcuts import render
from .decorators import (
    student_access_only,
    teacher_access_only,
    principal_access_only
)
 
 
def homePage(request, *args, **kwargs):
    context = {}
    return render(request, "user/homePage.html", context)
 
 
@student_access_only()
def studentView(request, *args, **kwargs):
    context = {}
    return render(request, "user/studentView.html", context)
 
 
@teacher_access_only()
def teacherView(request, *args, **kwargs):
    context = {}
    return render(request, "user/teacherView.html", context)
 
 
@principal_access_only()
def principalView(request, *args, **kwargs):
    context = {}
    return render(request, "user/principalView.html", context)


Step 10: user/urls.py

These URLs help to route to the respective views. 

Python3




from django.urls import path
from user import views as user_views
from django.contrib.auth.views import LoginView
 
app_name = "user_urls"
 
urlpatterns = [
    path("", user_views.homePage, name="home-page"),
    path("login-user/", LoginView.as_view(template_name="user/loginView.html"),
         name="login-user"),
    path("student-view/", user_views.studentView, name="student-view"),
    path("teacher-view/", user_views.teacherView, name="teacher-view"),
    path("principal-view/", user_views.principalView, name="principal-view"),
]


Step 11: CustomDecorator/settings.py

Also, set up the LOGIN_REDIRECT_URL for the login in settings.py

Python3




LOGIN_REDIRECT_URL = "user_urls:home-page"


Step 12: CustomDecorator/urls.py

Now set up the project urls.py to direct them to our user app.

Python3




from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("user.urls", namespace="user_urls")),
]


Step 13: Now, migrate the data to the database using the following command.  

python manage.py makemigrations
python manage.py migrate

Step 14: Open the Django shell and create three users, one student, one teacher, and one principal.

python manage.py shell

from user.models import UserAccount

UserAccount.objects.create_student(username = “student” , email = “studentmail@gmail.com” , password = “testing123”)

UserAccount.objects.create_teacher(username = “teacher” , email = “teachermail@gmail.com” , password = “testing123”)

UserAccount.objects.create_principal(username = “principal” , email = “principalmail@gmail.com” , password = “testing123”)

Step 19: Now run the server to test your project.

python manage.py runserver

Output:

 

Dominic Rubhabha-Wardslaus
Dominic Rubhabha-Wardslaushttp://wardslaus.com
infosec,malicious & dos attacks generator, boot rom exploit philanthropist , wild hacker , game developer,
RELATED ARTICLES

Most Popular

Recent Comments