Search code examples
pythondjangodjango-modelsdata-structuresdjango-admin

In Django admin, how do I manage complex user privileges?


Suppose you have this entity structure in Django with to-many relationships:

- Company
  - Division
    - Department
      - Unit

Every user is part of a Unit, so I managed the rights to create, edit and delete entities in Django Admin by assigning groups for users that had Department, Division or Company privileges. E.g. "DepartmentAdmin", "DivisionAdmin", etc. To determine the correct privilege, I just followed the Unit up the chain. Easy enough.

Now I have to refactor: users can now be members of more than one Unit (perhaps keeping their "main" Unit). They can have arbitrary rights to administer any other Department, Division, etc..

How would I best model that?

I have tried with a new entity Membership, a kind of join table with additional information about the role / privileges. Is this a good idea? I found that this creates a lot of dependencies elsewhere and determining the rights is quite complicated.

e.g.

class Membership(models.Model):
    user = models.ForeignKey(User,
       on_delete=models.CASCADE, related_name="other_departments")
    department = models.ForeignKey(Department, 
       on_delete=models.CASCADE, related_name="other_members")
    position = models.CharField(_("Position"), max_length=32, null=True, blank=True)

Or is there a better way by leveraging the existing admin rights management features of Django? How would you go about this?

Thanks!


Solution

  • To model the scenario where users can be members of more than one Unit and have arbitrary rights to administer different Departments, Divisions, and Companies, you can use a many-to-many relationship with an additional intermediate model for privileges and roles. Here's one way to approach it:

    • Keep the standard Django User model for user authentication.
    • Define the Unit model as you originally had it.
    • Create a model that represents a user's membership in different units and their roles/privileges within those units. This model will have fields to store information about the user's role, department, division, and company.
    from django.db import models
    
    class Membership(models.Model):
         user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="memberships")
         unit = models.ForeignKey(Unit, on_delete=models.CASCADE, related_name="memberships")
         position = models.CharField(_("Position"), max_length=32, null=True, blank=True)
    
    

    Permissions Model: You can create a model to define different permissions and roles (e.g., Admin, Manager, etc.) that users can have within a unit. This model can be used to grant and manage specific privileges.

    class Permission(models.Model):
        name = models.CharField(max_length=50)
        description = models.TextField()
    
    class MembershipRole(models.Model):
        membership = models.ForeignKey(Membership, on_delete=models.CASCADE)
        permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
    
    

    With this structure, you can:

    1. Associate users with multiple units, each having their specific roles and privileges. Define different permissions/roles (e.g., DepartmentAdmin, DivisionAdmin, etc.) in the Permission model.
    2. Associate users with these permissions by creating records in the MembershipRole model.
    3. Check a user's permissions based on their memberships and roles in units.

    In your views or controllers, you can determine a user's rights by querying their memberships and roles. You can also use Django's built-in permissions system in combination with this model to further control access to specific actions and views.

    This approach should provide you with the flexibility to handle users being members of multiple units and having arbitrary rights within each unit, while also making it easier to manage and query user privileges.