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