Search code examples
djangodjango-modelsdjango-permissions

Group permission connected to model list


My model:

class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)

POSITIONS = (
    ("volunteer", "volunteer"),
    ("employee", "employee"),
    ("manager", "manager"),
    ("director", "director"),
    ("vet", "vet")
)
position = models.CharField(choices=POSITIONS, max_length=15)
picture = models.ImageField(blank=True, null=True)

And I am using class based views. I want to make a permissions to access diffrent views depending to 'position' value. I have found dozen of solutions and now I am a bit confused. What is the best way to make what I want to do?


Solution

  • I would use custom decorators. You can use the @method_decorator(decorator, name='dispatch'). First you would create a decorators.py file in the app directory. Pretty much what happens is the method decorator is called before the view and passes the function to the first callable. What we actually do is we make the callable callable as position check and pass the position argument. Then we process the actual method decorator part which passes the function argument in _method_wrapper. At which point, we do our calculations just like we would in the view with the request variable in the _wrapper callable.

    # decorators.py
    from django.core.exceptions import PermissionDenied
    from app.models import UserProfile
    
    def position_check(position):
        def _method_wrapper(function):
            def _wrapper(request, *args, **kwargs):
                try:
                    user = UserProfile.object.get(user_id=request.user.id)
                except UserProfile.DoesNotExist:
                    raise PermissionDenied
                if user.position == position:
                    return function(request, *args, **kwargs)
                else:
                    raise PermissionDenied
            return _wrapper
        return _method_wrapper
    

    Now in your view you would call it like this.

    # views.py
    from app.decorators import position_check
    from django.utils.decorators import method_decorator
    from django.views.generic import TemplateView
    
    @method_decorator(position_check('manager'), name='dispatch')
    class HomeView(TemplateView):
        template_name = "home.html"
    

    The only issue I could see you running into is needing to inherit permissions. I wouldn't set your choices up the way you have them. I would set the actual value to a number and the choice portion to your human readable such as below. This would allow you to set permissions contingent upon the number value so if user.position >= 3 a director would then inherit permissions from manager.

    POSITIONS = (
        (1, "volunteer"),
        (2, "employee"),
        (3, "manager"),
        (4, "director"),
        (5, "vet")
    )