1

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?

J.Cleese
  • 25
  • 1
  • 6

1 Answers1

1

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")
)
Mitchell Walls
  • 1,041
  • 1
  • 8
  • 13
  • Thank you very much. I have a problem because even if user have right position to access- there is still forbiden. If you have a time can you also show me how to make it by using permissions and groups? I am learning, and found plenty of info how to make a groups and custom permission, but I didn`t found any info about connection these things with model to have a result which I want to achieve. Again thank you for help! – J.Cleese Jul 09 '18 at 19:47
  • I think that the issue might be at user = UserProfile.object.get(id=request.user.id) it might need to be user = UserProfile.object.get(user=request.user) I'll update answer for that. If this doesn't work please post your code. I have used nearly the same thing in my code before. – Mitchell Walls Jul 09 '18 at 19:51
  • Edit: I have solved a problem using user = UserProfile.objects.get(user_id=request.user.id) instead of yours solution in decorator. – J.Cleese Jul 09 '18 at 19:59