Search code examples
djangodjango-permissions

django UserPassesTestMixin with parameter


Here's working version with user_passes_test. But I would like to replace position > 1 with parameter. I found that, it's possible to do with UserPassesTestMixin. But I don't know how. Can anyone help?

models.py

class user_control(models.Model):
    user = models.ForeignKey(user, on_delete=CASCADE)    
    position = models.ForeignKey(position, on_delete=CASCADE)
    ...

views.py

def position_check(user):    
    position = 0 if user.is_anonymous else user_control.objects.values(
            'position__rank'
        ).get(
            user = user.id        
        )

    return True if position > 1 else False

@user_passes_test(position_check, login_url='loginPage')
def index(request):
   pass

@user_passes_test(position_check, login_url='loginPage')
def exportReview(request):
   pass

I was try:

class PositionCheckMixin(LoginRequiredMixin, UserPassesTestMixin):
    position_value = 2    

    def test_func(self):
        position = 0 if user.is_anonymous else user_control.objects.values(
                'position__rank'
            ).get(
                user = self.request.user.id        
            )
    
        return position > self.position_value

    def handle_no_permission(self):
        return redirect('loginPage')
    
class Review(PositionCheckMixin):
   position_value = 2

   def index(request):

Solution

  • You can make a decorator that takes for example an optional parameter:

    def position_check(function=None, position_value=1, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
        def check(user):    
            position = 0 if user.is_anonymous else user_control.objects.values(
                'position__rank'
            ).get(
                user=user.id        
            )
            return position > position_value
        
        actual_decorator = user_passes_test(
            check,
            login_url=login_url,
            redirect_field_name=redirect_field_name,
        )
        if function:
            return actual_decorator(function)
        return actual_decorator

    Then you can use the decorator with:

    @position_check(position_value=42, login_url='loginPage')
    def index(request):
        # …
        pass

    For a class-based view, using the UserPassesTestMixin mixin [Django-doc] is more convenient:

    class PositionCheckMixin(UserPassesTestMixin):
        position_value = 1
        
        def test_func(self):
            position = 0 if self.request.user.is_anonymous else user_control.objects.values(
                'position__rank'
            ).get(
                user=self.request.user.id
            )
    
            return position > self.position_value

    then you can mix this in a class-based view with:

    class MyView(PositionCheckMixin, View):
        position_value = 42
        
        # …