Open In App

Creating multiple user types and using proxy models in Python Django

Last Updated : 04 Sep, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

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:

  • The proxy model can exactly inherited from one non-abstract model class. 
  • It cannot inherit from multiple non-abstract model classes as it cannot provide connections between rows in different tables in the database. 
  • It can inherit from any number of abstract class models.  
  • The proxy model can inherit from any number of Proxy models that have the same non-abstract parent class. 

What can be done with proxy models in Django?

  • We can change the pythonic behavior of the model in such ways as changing the ordering, annotating the model with a different name than the parent, etc.
  • We can have a proxy model with a customized query_set to get relevant data according to the model.
  • We can have a variety of methods, properties, and functions that are unique to that model.
  • We can create multiple types of users which inherit from your base user model and they all can log in, authenticate, and perform different functions.

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.

Python3




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

Python3




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

Python3




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: 

  • In the UserAccount model, created a new class Types(models.TextChoices) which will provide us with choices of choosing the type of user for our proxy model inside the UserAccount. 
  • Created a field type that tells the type of the user, the default is set to TEACHER, and the email field is unique for an authentication system. Here we defined 3 fields,i.e. is_superuser, is_admin, and is_staff, this provides permissions for the users.
  • Created two new fields is_student, and is_teacher which are just boolean fields that tell if the user is a teacher or a student.
  • While saving it if there is no defined type for the user set it to TEACHER.
  • We also created two functions create_user and create_superuser which help to create users and super users with different permissions. This method create_user will be used in our proxy models to create users, this is done because there is no way for hashing the password in proxy models.

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

Python3




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.

Python3




# 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. 

Python3




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

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

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. 

 



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads