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.