I'm new to Django and StackOverflow so please be a bit chill with me! I will update the code and everything as per requests.
I'm currently working on a system where students can join groups called Societies. These Societies have a ManyToManyField relationship with Users. I have been trying to think of a way to build a system where within each Society, there are Leaders and general members. Leaders can promote members to be fellow leaders, delete posts in the society, or even kick members of the society.
I would love some guidance in the right direction on how to structure (or re-structure!) my code. Specifically, I would like to know how to incorporate a "Leader" group of users with custom permissions like the one described above, that I can then use in my templates. Thank you!
class Society(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
members = models.ManyToManyField(User, verbose_name=("Members"))
def __str__(self):
return f"{self.name}"
class SocPost(models.Model):
title = models.CharField(max_length = 100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
society = models.ForeignKey(Society, on_delete=models.CASCADE, related_name="posts")
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'society_id':self.society.id, 'pk':self.pk})
Simplest solution is to use a through model:
class SocietyMembership(models.Model)
member = models.ForeignKey(User, related_name='society_membership')
society = models.ForeignKey('Society')
is_leader = models.BooleanField(default=False)
class Society(models.Model):
name = models.CharField(max_length = 200)
description = models.TextField()
members = models.ManyToManyField(
User,
verbose_name=("Members"),
related_name='societies',
through=SocietyMembership
)
def __str__(self):
return f"{self.name}"
This avoids extra queries injected by ContentTypes and the need to create one leader group per society in order to separate permissions from one group to the next.
Update:
Model update:
class Society(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
members = models.ManyToManyField(
User,
verbose_name=_("Members"),
related_name="societies",
through=SocietyMembership,
)
#property
def leaders(self):
return self.memberships.filter(is_leader=True)
def user_is_leader(self, user: User):
return self.memberships.filter(is_leader=True, member=user).exists()
def is_member(self, user: User):
return self.members.filter(pk=user.id).exists()
def __str__(self):
return f"{self.name}"
def enroll(self, user: User, as_leader: bool = False):
# Django >= 2.2, which it should be in July 2020
self.members.add(user, through_defaults={"is_leader": as_leader})
def promote(self, user: User) -> bool:
try:
membership = self.memberships.get(member=user)
except SocietyMembership.DoesNotExist:
return False
membership.is_leader = True
membership.save()
return True
def kick(self, user: User):
self.memberships.filter(member=user).delete()
Example of a views with permission check:
from django.views import generic
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from . import models
class SocietyList(LoginRequiredMixin, generic.ListView):
model = models.Society
template_name = "main/society-list.html"
context_object_name = "societies"
class SocietyDetail(UserPassesTestMixin, generic.DetailView):
model = models.Society
template_name = "main/society-detail.html"
context_object_name = "society"
def test_func(self):
# self.object will be an instance of Society
if not hasattr(self, "object"):
setattr(self, "object", self.get_object())
return self.object.is_member(self.request.user)
It seems like you want your 'leaders' to be an admin, denoted by the 'is_superuser' field in the default django user model. What you described are typical admin activities in Django (and can easily be done in the django admin panel). Create one of these superusers by executing python manage.py createsuperuser and then you will have access to the admin panel where you can customize everything you need, and promote more users to admin level. If you are looking for some level of leadership between basic user and admin, you can create users with custom permissions dependent on the user_permissions field, denoted by some sort of variable which you add to the django default user model using a 1:1 relationship.
Related
In my pervious question I asked how I can automatically save the user submitting the form. I found the form_valid method to be the best in that case. However in my models I also have a user profile model like this
models.py
....
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
title = models.CharField(max_length=24)
first_name = models.CharField(max_length=35)
last_name = models.CharField(max_length=35)
email = models.EmailField(max_length=64)
phone_number = models.CharField(max_length=12)
department = models.ForeignKey(Department,null=True,on_delete=models.SET_NULL)
supervisor = models.ForeignKey('self',blank=True,null=True,on_delete=models.SET_NULL)
...
As you can see I used the One to One method to make my UserProfile
As before in my models.py I have my reports model
...
class Report(models.Model):
id = models.UUIDField(primary_key=True,default=uuid.uuid1,editable=False)
department = models.ForeignKey(Company,null=True,on_delete=models.SET_NULL)
user= models.ForeignKey(User,on_delete=models.PROTECT)
submission_date= models.DateField(auto_now=True) #invisible to user
submission_time = models.TimeField(auto_now=True) #invisible to ,user
date = models.DateField(default=now,blank=False)
time = models.TimeField(default=now,blank=False,help_text="hh:mm:ss")
location = PlainLocationField()
building = models.ForeignKey(bld,null=True,on_delete=models.SET_NULL)
size = models.PositiveIntegerField()
notes = models.TextField(blank=True)
def __str__(self):
return f'{self.date} {self.time} ({self.department})
...
My question how I can make it so that the department field will load from the user profile? I would hope to eventually make it possible for users in the same department to be able to view and update each others Reports.
As before:
form.py
class ReportForm(forms.ModelForm):
class Meta:
model = Report
fields = '__all__'
location = PlainLocationField()
def redirect():
return redirect("Report")
views.py
class ReportCreate(LoginRequiredMixin,CreateView):
Template = "templates\reports\Report.html"
model = Report
fields = '__all__'
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.save()
return super(ReportCreate, self).form_valid(form)
def get_success_url(self):
return reverse('Report')
def get_absolute_url(self):
return reverse('Report', kwargs={'pk':self.pk})
I advise you to use related_name in your ForeignKeys. Set the department field of both models as following:
class Profile(models.Model):
...
department = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL, related_name='profiles')
...
class Report(models.Model):
...
department = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL, related_name='reports')
...
From now on, Department objects that are related to User.Profile you can access like that:
Department.profiles.all() # it returns QuerySet of all related to Department Profile objects
Department.reports.all() # it returns QuerySet of all related to Department Report objects
And you can use it in making QuerySet for user:
Report.objects.filter(department=self.request.user.profile.department)
# it returns all Report objects, that have exactly the same department as the user
Or using our new relationship:
department = self.request.user.profile.department
reports_for_user = department.reports.all()
But I can see one problem. You are using Company model for ForeignKey in Report. It has to be the same Department model for both Profile and Report models for such easy option to work. Also you definitely should not mix naming in single project. You can set relation with Company as another field:
company = models.ForeignKey(Company, null=True, on_delete=models.SET_NULL)
Here are some steps to help you autofill some fields:
Get the user from self.request.user. How to access current user in Django class based view
Get the profile: get user profile in django
Pass the required fields as context variables: https://www.geeksforgeeks.org/how-to-pass-additional-context-into-a-class-based-view-django/
Pass it into javascript. How can I pass my context variables to a javascript file in Django?
Set the value like this: Set the value of an input field
DONE!
I couldn't find a solution to my problem and would appreciate comments/help on this.
I would like to develop a multiple user type model in Django, along the lines of this video where the author is using Django Proxy Models.
Situation
I have a list of XX projects (proj01, proj02 , projXX, ...).
All these projects have their specific page that can be accessed through a specific url mysite/projXX/
I have multiple users: Adam, Bob, Caroline, Dany, Ed, ...
Each user can have several roles according to the project they are working on (e.g. manager, developer, documentarist, reviewer, editor, ...)
A user can have a different role according to the project. E.g. Adam can be reviewer on proj01 but editor on proj02 while Bob can be editor on proj01 but reviewer on proj02, etc..
I started defining multiple user types in the models.py file below (only reviewer and editor roles):
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
class User(AbstractUser):
class Types(models.TextChoices):
EDITOR= "EDITOR", "Editor"
REVIEWER = "REVIEWER", "Reviewer"
base_type = Types.EDITOR
type = models.CharField(
_("Type"), max_length=50, choices=Types.choices, default=base_type
)
name = models.CharField(_("Name of User"), blank=True, max_length=255)
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
def save(self, *args, **kwargs):
if not self.id:
self.type = self.base_type
return super().save(*args, **kwargs)
class EditorManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(type=User.Types.EDITOR)
class ReviewerManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(type=User.Types.REVIEWER)
class EditorMore(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
gadgets = models.TextField()
class Editor(User):
base_type = User.Types.EDITOR
objects = EditorManager()
class Meta:
proxy = True
def edit(self):
return "Edition in progress"
class ReviewerMore(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
model = models.CharField(max_length=255)
make = models.CharField(max_length=255)
year = models.IntegerField()
class Reviewer(User):
base_type = User.Types.REVIEWER
objects = ReviewerManager()
#property
def more(self):
return self.reviewermore
class Meta:
proxy = True
def review(self):
return "In Review"
Question:
What is the best way to handle the fact that the role of the user can change according to the project page he/she is visiting?
Example: If Adam is logged in and visits the page mysite/proj01/ I would like him to access only the content allowed for a reviewer while if Adam visit mysite/proj02/, I would like the user to see only the content allowed to the editor.
Ideally, I would like each user to have its unique entry in the user database. I was thinking that the project-dependent role level could be stored as a dictionary? For example:
{'proj01':'reviewer', 'proj02':'editor', 'projxx': 'roleY', ... }
How would combine this user model and the list of project-dependent permissions?
Edit 02/07/21
Add example files for a project app, models.py and views.py :
# projects/models.py
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
class Project(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("project_detail", args=[str(self.id)])
# projects/views.py
from django.contrib.auth.mixins import (
LoginRequiredMixin,
UserPassesTestMixin,
)
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from django.urls import reverse_lazy
from .models import Project
class ProjectListView(LoginRequiredMixin, ListView):
model = Project
template_name = "project_list.html"
class ProjectDetailView(LoginRequiredMixin, DetailView):
model = Article
template_name = "project_detail.html"
class ProjectUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Project
fields = (
"title",
"body",
)
template_name = "project_edit.html"
def test_func(self):
obj = self.get_object()
return obj.author == self.request.user
If you have projects as a model. You can add custom permissions to the model. Then assign those permissions to your users appropriately for each project (actually easily add/remove permissions too).
Then use either user_passes_test or permissions_required in your views/template to restrict what users can see/access/edit.
class Project(models.Model):
project_name = ...
class RoleType(models.Model):
role_name = models.CharField
# Permission boolean flags
can_edit = models.Boolean
can_view = models.Boolean
class ProjectRole(models.Model):
project = models.ForeignKey('Project', ...)
role = models.ForeignKey('RoleType', ...)
user = models.ForeignKey('User', ...)
Now you can reverse lookup based on project or user
# To show all assigned users and their roles for a project
foo_project = Project.objects.get(project_name='foo')
project_roles = ProjectRole.objects.filter(project=foo_project)
You can also restrict your views and templates by roles and their permissions boolean flags.
Create groups that defines your Roles, for example : Group1: Editor, Group2:Manager and so on
Assign each user to the specified group ( you can do it in python manage.py shell or in admin panel )
Add restrictions on the view, for example: /mysite/projx/ view is restricted to groupA, you can check the following question that helps you with this point: https://stackoverflow.com/a/4789038/13508969
For example:
GroupA : GlobalEditor ( Bob can edit in projx and projy , and can only view projz )
GroupB : viewonly ( Adam can only view the content of the projs )
and so on
I have been using Django for quite a while but never have I thought of this until now.
Currently, I have a project that contains different user levels. Usually, in my past experience, I only developed systems using Django with only two user levels which are superuser and normal/regular user. So my question is what are the effective ways to present these different user levels in the model/database? Here, I'm going to use a school system as an example and also provide some of my initial thoughts on implementing it.
User levels:
Admin (superuser & staff)
Principal
Teacher
Students
Method #1: Add new tables based on each user level
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
user = models.CharfieldField(max_length = 10, unique = True)
class Admin(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Pricipal(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
Method #2: Add additional user types attributes in the User model
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
user = models.CharfieldField(max_length = 10, unique = True)
is_superuser = models.BooleanField(default = False)
is_staff = models.BooleanField(default = False)
is_principal = models.BooleanField(default = False)
is_teacher = models.BooleanField(default = False)
is_student = models.BooleanField(default = False
'''
User table in DB:
user | is_superuser | is_staff | is_principal | is_teacher | is_student
'''
My thoughts:
In Method #1, as the built-in User model has two fields, is_staff and is_superuser, Is it possible to implement/change the fields into a SuperUser/Admin table as in the example above? This means that when I create an admin/superuser, I want it to add a new row into the Admin table, instead of adding a new user and updating the user's is_superuser and is_staff fields into 1 in the built-in User model.
In Method #2, the problem with it is that tables with different access privileges are directly connected to it. For example, Salary model (which cannot be accessed by Student user) has a direct link with the User model (contains Student user).
I hope I am able to get some insights and also a proper effective way of implementing this so that to prevent any implementation inconvenience and mistakes in the future. Thank you very much.
I think you are in the right path with method #2. It is lighter, and more straightforward.
I would not use a custom "user-like" model for each permission level. Over-complicated, does not scale, and multiply the number of queries, with no very benefit for your problem. Not your UML schema but its content must guarantee your permission requirements.
If the permission levels are not mutual-exclusive :
from django.db import models
from django.contrib.postgres.fields import ArrayField
class User(AbstractUser):
ADMIN = 0
PRINCIPLE = 1
TEACHER = 2
STUDENT = 3
USER_LEVEL_CHOICES = (
(ADMIN, "Admin"),
(PRINCIPLE, "Principle"),
(TEACHER, "Teacher"),
(STUDENT, "Student"),
)
status = ArrayField(
models.IntegerField(choices=USER_LEVEL_CHOICES, blank=True, default=STUDENT),
)
But you need to have a wider reflexion.
I think you are talking about two separate problems : polymorphism, and permissions
Polymorphism :
Polymorphism is the ability of an object to take on many forms. For a Django model, it can be done with many strategies : OneToOneField -as you mentioned- multi-table inheritance, abstract models, or proxy-models.
Very good resources : this article, and Django doc about model inheritance
This very complex problem all refer to : how much your several forms of a same entity are similar, or different. And which operations are particularly similar or different (data shape, querying, permission, ...etc)
Permissions design :
You can choose among several patterns
Model-oriented permission : A user is granted "add", "view", "edit" or "delete" permission to a Model. This is done in Django with the built-in Permission model, that have a ForeignKey to ContentType
Object-oriented permission : A user is granted "add", "view", "edit" or "delete" permission for each Model instance. Some packages provides this ability, django-guardian for example.
Rule-oriented permission : A user is granted permission to a Model instance through custom logic instead of M2M table. The django rules package provide this kind of architecture.
You can create from AbstractUser (a full User model, complete with fields, including is_superuser and is_staff) a Profile and then, once you have the profile, give the chance of users to create other type of profile (Student, Teacher or Principle) which could have functionalities of its own.
For instances, in your models.py
class Profiles(AbstractUser):
date_of_birth = models.DateField(max_length=128, blank=True, null=True, default=None, verbose_name=_(u'Date of birth'))
principle = models.OneToOneField(Principles, null=True, blank=True, verbose_name=_(u'Principles'), on_delete=models.CASCADE)
teacher = models.OneToOneField(Teachers, null=True, blank=True, verbose_name=_(u'Teachers'), on_delete=models.CASCADE)
student = models.OneToOneField(Students, null=True, blank=True, verbose_name=_(u'Students'), on_delete=models.CASCADE)
class Meta:
db_table = 'profiles'
verbose_name = _('Profile')
verbose_name_plural = _('Profiles')
To that model you can add class methods, such as
def is_teacher(self):
if self.teacher:
return True
else:
return False
Then, your Teachers model could look like this
class Teachers(models.Model):
image = models.FileField(upload_to=UploadToPathAndRename(settings.TEACHERS_IMAGES_DIR), blank=True, null=True, verbose_name=_('Teacher logo'))
name = models.CharField(blank=False, null=False, default=None, max_length=255, validators=[MaxLengthValidator(255)], verbose_name=_('Name'))
street = models.CharField( max_length=128, blank=False, null=True, default=None, verbose_name=_('Street'))
created_by = models.ForeignKey('Profiles', null=True, blank=True, on_delete=models.SET_NULL)
One of the methods that I used in several projects is this (pseudo code):
class User(AbstractUser):
ADMIN = 0
PRINCIPLE = 1
TEACHER = 2
STUDENT = 3
USER_LEVEL_CHOICES = (
(ADMIN, "Admin"),
(PRINCIPLE, "Principle"),
(TEACHER, "Teacher"),
(STUDENT, "Student"),
)
user_level = models.IntgerField(choices=USER_LEVEL_CHOICES)
def lvl_decorator():
def check_lvl(func):
def function_wrapper(self, actor, action_on, *args, **kwargs):
if 'action_lvl' not in action_on: # then action_on is user
if actor.user_lvl < action_on.user_lvl:
return True
return False
else: # then action_on is action of some kind for that user (you can add action_lvl to ... and pas them to this wapper)
if actor.user_lvl < action_on.action_lvl:
return True
return False
return function_wrapper
return check_lvl
Then you can write wrapper function with this logic for any action check if action level is bigger than user level e.g.: if someone wants to change superuser password he/she should be logged-in with level-0-user but for changing normal user's password he/she should be level 0, 1. This logic also can be applied to class, functions, etc actions.
Create base class and then add lvl_decorator to it then inherent from it => this keeps your code super clean and prevents further copy paste.
example of what i mean:
def lvl_decorator():
def check_lvl(func):
def function_wrapper(self, actor, action_on, *args, **kwargs):
if 'action_lvl' not in action_on: # then action_on is user
if actor.user_lvl < action_on.user_lvl:
return True
return False
else:
if actor.user_lvl < action_on.action_lvl:
return True
return False
return function_wrapper
return check_lvl
class BaseClass(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = lvl_decorator()
return type.__new__(cls, name, bases, local)
# in other locations like views.py use this sample
class FooViewDjango(object, ApiView): # don't remove object or this won't work, you can use any Django stuff you need to inherent.
__metaclass__ = BaseClass
def baz(self):
print('hora hora')
Use this base class in any where you want.
As title, I have 3 types of User and each User can have more than one role.
from django.contrib.auth.models import AbstractUser, User
from django.db import models
from django.db.models import CASCADE
from hospital.models import Hospital
class Role(models.Model):
'''
The Role entries are managed by the system,
automatically created via a Django data migration.
'''
DOCTOR = 1
DIRECTOR = 2
PATIENT = 3
ROLE_CHOICES = (
(DOCTOR, 'doctor'),
(DIRECTOR, 'director'),
(PATIENT, 'patient'),
)
id = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, primary_key=True)
def __str__(self):
return self.get_id_display()
class User(AbstractUser):
roles = models.ManyToManyField(Role)
def __str__(self):
return self.roles
class Doctor(models.Model):
# role = models.OneToOneField(User, on_delete=CASCADE)
career = models.TextField(blank=True, max_length = 1000)
class Director(models.Model):
# role = models.OneToOneField(User, on_delete=CASCADE)
members = models.ManyToManyField(Doctor)
class Patient(models.Model):
# personal information like above.
https://simpleisbetterthancomplex.com/tutorial/2018/01/18/how-to-implement-multiple-user-types-with-django.html
I'm creating a model, but I do not know how to set the key.
I made it by referring to the above site. The director owns the doctor and can authorize it.
But I do not know how to give a key to a doctor or director.
If you I to comment, I get an error.
I hope you can help me.
Assuming that you want to set the default roles for the 3 derived classes.
Taking slight inspiration from this post.
Since roles is a many-to-many relation, you can override the model's save method (you can use signals for this as well, but wouldn't recommend that in your case).
e.g.
class Doctor(models.Model):
def save(self, *args, **kwargs):
if not self.id: # object creation
should_add_role = True
super(Doctor, self).save(*args, **kwargs) # do the usual
if should_add_role: # add our defaults
self.roles.add(Role.objects.get_or_create(id=Role.DOCTOR))
I am very new to Python and Django. I am trying to setup user model for different roles like Agents, Brokers, Company and Customer. Each of these roles can register with the site as a user. Then Agents and Brokers can have public facing profile pages.
Do I have to use custom user model or built in user model will work? I have additional properties like license, location, languages, photo etc for Agents and Brokers.
class MyUser(AbstractBaseUser):
AGENTS = 'AG'
BROKERS = 'BR'
COMPANY = 'CP'
CUSTOMER = 'CM'
ROLE_IN_CHOICES = (
(AGENTS, 'Agent'),
(BROKERS, 'Broker'),
(COMPANY, 'Company'),
(CUSTOMER, 'Customer'))
first_name = models.CharField(max_length=100, blank=True)
second_name = models.CharField(max_length=100, blank=True)
middle_name = models.CharField(max_length=100, blank=True)
dob = models.DateField(blank=True, null=True)
phone = models.CharField(max_length=10)
secondary_phone = models.CharField(max_length=10, blank=True, null=True)
......
#property
def is_agent(self):
return self.role in (self.AGENTS)
#property
def is_customer(self):
return self.role in (self.CUSTOMER)
#property
def is_broker(self):*
return self.role in (self.BROKER)
#property
def is_company(self):
return self.role in (self.COMPANY)
....
Can I not use base User model and achieve same? Am I on write track?
How do I create public facing pages for these roles (Agents, Brokers)?
This is my first attempt with Python and Django. I am using Django 1.7.7 with Python 3.4
You should extend from the Django User model instead and add the extra fields you need:
from django.contrib.auth.models import User as Auth_User
class User(Auth_User):
# add your extra fields here like roles, etc
phone = CharField(max_length=20, null=True, blank=True)
# add your extra functions
def extra_user_function(self):
return "This is an extra function"
This way you have your own fields and also the Django User fields...
After migrating, if you check the database, you'll have auth_user and your_app_user tables.
Just bare in mind that request.user will only give you the super fields... In order to get the extended class you'll need
User.objects.get(id=request.user.id)
The latest will only have the extra fields and its id will be the same as the auth.User if you don't add any auth_user by itself.
Attention: this is important!
Otherwise request.user.id and your_app.User.id don't match, therefore User.objects.get(id=request.user.id) won't work and you'll have to query the db to find the your_app.User.id
User.object.get(user_ptr_id = request.user.id)
Other things to consider
This will work:
# you_app.User objects gets vars from auth.User
user = User.objects.get(id=request.user.id)
first_name = user.first_name
But this won't work
# auth.User trying to get a function from your_app.User
user = request.user
user.extra_user_function()
So the User model could be something like this:
import os
from django.contrib.auth.models import User as Django_User
from django.db.models import CharField, ImageField
class User(Django_User):
phone = CharField(max_length=20, null=True, blank=True)
observations = CharField(max_length=2048, null=True, blank=True)
picture = ImageField(upload_to='users', default='default/avatar.jpg')
class Meta:
# adding extra permissions (default are: add_user, change_user, delete_user)
permissions = (
("access_user_list", "Can access user list"),
("access_user", "Can access user"),
)
ordering = ["-is_staff", "first_name"]
Instead of creating roles on the user, Django already has groups, so you should use them.
The groups follow the same logic:
from django.contrib.auth.models import Group as Auth_Group
from django.db import models
class Group(Auth_Group):
observations = models.CharField(max_length=2048, null=True, blank=True)
def get_users_in_group(self):
return self.user_set.filter(is_active=1).order_by('first_name', 'last_name')
def count_users_in_group(self):
return self.user_set.count()
def __unicode__(self):
return self.name
class Meta:
permissions = (
("access_group_list", "Can access group list"),
("access_group", "Can access group"),
)
ordering = ["name"]
You can clear / add users to a group:
user.groups.clear()
user.groups.add(group)
Clear / add permissions to the group:
group.permissions.clear()
group.permissions.add(permission)
There is also a decorator to check if a user has permissions
from django.contrib.auth.decorators import permission_required
#permission_required(("users.change_user","users.access_user",))
def your_view(request):
...
I've tried many things in the past, but I guess this is the way to go.
If you really need roles, and a user can have more than one role, mayb the best thing would be to create a model Role and add that to the user has a ManyToMany Field
roles = ManyToManyField(Role)
but you could do that with groups