No pude encontrar una solución a mi problema y agradecería comentarios/ayuda al respecto.
Me gustaría desarrollar un modelo de tipo de usuario múltiple en Django, en la línea de este video donde el autor está usando Django Proxy Models .
Tengo una lista de XX proyectos ( proj01 , proj02 , projXX , ...). Todos estos proyectos tienen su página específica a la que se puede acceder a través de una url específica mysite/projXX/
Tengo múltiples usuarios: Adam, Bob, Caroline, Dany, Ed,...
Cada usuario puede tener varios roles según el proyecto en el que esté trabajando (por ejemplo, gerente, desarrollador, documentalista, revisor, editor, ...)
Un usuario puede tener un rol diferente según el proyecto. Por ejemplo, Adam puede ser revisor en proj01 pero editor en proj02 mientras que Bob puede ser editor en proj01 pero revisor en proj02, etc.
Empecé a definir varios tipos de usuarios en el archivo models.py a continuación (solo roles de revisor y editor):
# 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"¿Cuál es la mejor manera de manejar el hecho de que el rol del usuario puede cambiar según la página del proyecto que está visitando?
Ejemplo: si Adam está conectado y visita la página mysite/proj01/ , me gustaría que acceda solo al contenido permitido para un revisor, mientras que si Adam visita mysite/proj02/ , me gustaría que el usuario vea solo el contenido permitido para el editor.
Idealmente, me gustaría que cada usuario tuviera su entrada única en la base de datos de usuarios. Estaba pensando que el nivel de rol dependiente del proyecto podría almacenarse como un diccionario. Por ejemplo:
{'proj01':'reviewer', 'proj02':'editor', 'projxx': 'roleY', ... }¿Cómo combinaría este modelo de usuario y la lista de permisos dependientes del proyecto?
Agregue archivos de ejemplo para una aplicación de project , models.py y 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.userSi tienes proyectos como modelo. Puede agregar permisos personalizados al modelo. Luego, asigne esos permisos a sus usuarios de manera adecuada para cada proyecto (en realidad, también agregue o elimine permisos fácilmente).
Luego use user_passes_test o permisos_requeridos en sus vistas/plantilla para restringir lo que los usuarios pueden ver/acceder/editar.
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', ...)Ahora puede realizar búsquedas inversas basadas en proyectos o usuarios
# 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)También puede restringir sus vistas y plantillas por roles y sus banderas booleanas de permisos.
Por ejemplo: GroupA: GlobalEditor (Bob puede editar en projx y projy, y solo puede ver projz)
GroupB: viewonly (Adán solo puede ver el contenido de los proyectos) y así sucesivamente