Open In App

Creating multiple user types and using proxy models in Python Django

In this article, we will understand the concept of the proxy model and the implementation of multiple user types depending on the requirements of your application in Django.

What is the Proxy model in Django?

An inherited proxy model can have a few extra functionalities, methods, and different behaviors than the parent model as defined by the creator or programmer. This can be useful in situations like having multiple types of users inherited from the same User Model, defining new functions for the proxy (new inherited model) which are only meant to be used by the proxy (new inherited model), and so forth. New fields cannot be added to proxy models, the limitation of the proxy model is that you cannot have custom fields there.



Properties for creating a proxy model:

What can be done with proxy models in Django?

File Structure



 

Stepwise Implementation

Step 1: Create a Django Project named ProxyModel.

django-admin startproject ProxyModel

Step 2: Move to the project folder, then create an app named proxymodelapp.

python manage.py startapp proxymodelapp

Step 3: After creating the app, Go to the setting.py and register the app in the INSTALLED_APPS.




INSTALLED_APPS = [
    # add to the installed apps .  
    'proxymodelapp.apps.ProxymodelappConfig'
    
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Step 4: Set URL for the ProxyModel project.

 

Step 4: Set URL for the proxymodelapp app.

 

Step 5: Paste the code in proxymodelapp/views.py




from django.shortcuts import render
  
def homePage(request) : 
    return render(request ,  "proxymodelapp/homePage.html" )

Step 6: We will create a custom user name as UserAccount with its object named UserAccountManager with fields and permissions. It will give permission to different users. Here we will create two types of users Teacher and a Student and we will add two more fields other than basic fields in the model that are is_teacher and is_student as they are not required, but for minor issues, this can be helpful.

proxymodelapp/models.py




from django.db import models
from django.contrib.auth.models import AbstractBaseUser , BaseUserManager
  
class UserAccountManager(BaseUserManager):
    def create_user(self , email , password = None):
        if not email or len(email) <= 0
            raise  ValueError("Email field is required !")
        if not password :
            raise ValueError("Password is must !")
          
        user = self.model(
            email = self.normalize_email(email) , 
        )
        user.set_password(password)
        user.save(using = self._db)
        return user
      
    def create_superuser(self , email , password):
        user = self.create_user(
            email = self.normalize_email(email) , 
            password = password
        )
        user.is_admin = True
        user.is_staff = True
        user.is_superuser = True
        user.save(using = self._db)
        return user
      
class UserAccount(AbstractBaseUser):
    class Types(models.TextChoices):
        STUDENT = "STUDENT" , "student"
        TEACHER = "TEACHER" , "teacher"
          
    type = models.CharField(max_length = 8 , choices = Types.choices , 
                            # Default is user is teacher
                            default = Types.TEACHER)
    email = models.EmailField(max_length = 200 , unique = True)
    is_active = models.BooleanField(default = True)
    is_admin = models.BooleanField(default = False)
    is_staff = models.BooleanField(default = False)
    is_superuser = models.BooleanField(default = False)
      
    # special permission which define that
    # the new user is teacher or student 
    is_student = models.BooleanField(default = False)
    is_teacher = models.BooleanField(default = False)
      
    USERNAME_FIELD = "email"
      
    # defining the manager for the UserAccount model
    objects = UserAccountManager()
      
    def __str__(self):
        return str(self.email)
      
    def has_perm(self , perm, obj = None):
        return self.is_admin
      
    def has_module_perms(self , app_label):
        return True
      
    def save(self , *args , **kwargs):
        if not self.type or self.type == None
            self.type = UserAccount.Types.TEACHER
        return super().save(*args , **kwargs)

Code explanation: 

Step 7: Set the authentication model in setting.py which will be used for all the work.

settings.py

AUTH_USER_MODEL = "proxymodelapp.UserAccount"

 

Step 8: After creating the user auth model, the main task is to create multiple user types with the proxy model with their respective managers, if we do not create managers for our proxy models they will inherit from the parent model. In models.py add the two models.

proxymodelapp/models.py




class StudentManager(models.Manager):
    def create_user(self , email , password = None):
        if not email or len(email) <= 0
            raise  ValueError("Email field is required !")
        if not password :
            raise ValueError("Password is must !")
        email  = email.lower()
        user = self.model(
            email = email
        )
        user.set_password(password)
        user.save(using = self._db)
        return user
      
    def get_queryset(self , *args,  **kwargs):
        queryset = super().get_queryset(*args , **kwargs)
        queryset = queryset.filter(type = UserAccount.Types.STUDENT)
        return queryset    
        
class Student(UserAccount):
    class Meta : 
        proxy = True
    objects = StudentManager()
      
    def save(self , *args , **kwargs):
        self.type = UserAccount.Types.STUDENT
        self.is_student = True
        return super().save(*args , **kwargs)
      
class TeacherManager(models.Manager):
    def create_user(self , email , password = None):
        if not email or len(email) <= 0
            raise  ValueError("Email field is required !")
        if not password :
            raise ValueError("Password is must !")
        email = email.lower()
        user = self.model(
            email = email
        )
        user.set_password(password)
        user.save(using = self._db)
        return user
        
    def get_queryset(self , *args , **kwargs):
        queryset = super().get_queryset(*args , **kwargs)
        queryset = queryset.filter(type = UserAccount.Types.TEACHER)
        return queryset
      
class Teacher(UserAccount):
    class Meta :
        proxy = True
    objects = TeacherManager()
      
    def save(self  , *args , **kwargs):
        self.type = UserAccount.Types.TEACHER
        self.is_teacher = True
        return super().save(*args , **kwargs)

Code explanation: 

Notice, that we created the Student and Teacher model from inheriting the UserAccount and then we set the proxy keyword to True, in order to classify the model to be proxy, and we have overridden the save function, where we set the type of the UserAccount to be their respective types and save it every time when the function is saved. It is because you might change it from the admin panel and it will change it to another type to overcome that, save the type every time.  We have created respective managers for the Teacher and Student model which return the queryset of the respective models which contain the objects of their respective types. 

In managers of each of the users, Teacher, and Student we have added the method create_user  which is used to create the student and the teacher.

Step 9: If you want that to change or set the type only once, use this code for the save function instead of the above code snippet for the save function.




# for teacher snippet
def save(self  , *args , **kwargs):
    if not self.id or self.id == None
        self.type = UserAccount.type.TEACHER
    return super().save(*args , **kwargs)
    
# for student snippet 
def save(self  , *args , **kwargs):
    if not self.id or self.id == None
        self.type = UserAccount.type.STUDENT
    return super().save(*args , **kwargs)

Step 8: Make sure to register all the models to the admin panel.

 

Step 10: In cmd write the following commands to migrate data to the database.

python manage.py makemigrations
python manage.py migrate

 

Step 11: Now create a superuser who can log in to the app and can see the functionalities, for that we will create a superuser with the email: testingmail@gmail.com with password 123.

python manage.py createsuperuser

Step 12: Move to the Django shell in the terminal. Also, we will create multiple users of both types from the Django shell.

python manage.py shell

Here,  we have created 4 users in the database and these can log in and have multiple functionalities. 




from proxymodelapp.models import *
user1 = Teacher.objects.create_user(
  email = "teachermailone@gmail.com" , password = "password")
user1.save()
user2 = Teacher.objects.create_user(
  email = "teachermailtwo@gmail.com" , password = "password")
user2.save()
user3 = Student.objects.create_user(
  email = "studentmailone@gmail.com" , password = "password")
user3.save()
user4 = Student.objects.create_user(
  email = "studentmailtwo@gmail.com" , password = "password")
user4.save()

Step 13: Create an HTML page for the login page. This template helps to log in to the user by entering the email and its password. and as the user logs in, it is redirected to the homepage.

proxymodelapp/templates/proxymodelapp/loginPage.html




<!DOCTYPE html>
<html>
  <body>
    <form method="post">
      {% csrf_token %}
      {{form.as_p}}
      <br />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>

Step 14: Create an HTML page for the home page. This template shows who is logged in, what his email and what is his type of user. If the user is authenticated, the details will be shown.

proxymodelapp/templates/proxymodelapp/homePage.html




<!DOCTYPE html>
<html>
  <body>
    <hr>
    <br>
    {% if request.user.is_authenticated %}
    The current_user is  : {{request.user.email}}
    <br>
    <br>
    The type of the user is  : {{request.user.type}}
    {% endif %}
    <br />
    <br />
    <a href="{% url 'login-user' %}"> Login </a>
    <br />
    <hr>
  </body>
</html>

Output: All three are different models and you can log in from any of them. 

 


Article Tags :