Check permissions using the decorator

Note

A decorator is not the ultimate painkiller, if you need to deal with complex permission handling, take a look at Check permissions in python code.

The decorator syntax

Lets start with an example permission:

class FlatpagePermission(permissions.BasePermission):
    label = 'flatpage_permission'
    checks = ('can_do_foo',)

    def can_do_foo(self):
        # ...

authority.register(Campaign, FlatpagePermission)

A decorator for such a simple view would look like:

from authority.decorators import permission_required

@permission_required('flatpage_permission.can_do_foo')
def my_view(request):
    # ...

The decorator automatically takes the user object from the view’s arguments and calls can_do_foo. If this function returns True, the view gets called, otherwise the user will be redirected to the login page.

Passing arguments to the permission

You can pass any arguments to the permission function. Assumed our permission function looks like this:

def can_do_foo(self, view_arg1, view_arg2=None):
    # ...

Our decorator can grab the arguments from the view and passes it to the permission function. Just take the arguments from the view and place them as a string on the decorator:

@permission_required('flatpage_permission.can_do_foo', 'arg1', 'arg2')
def my_view(required, arg1, arg2):
    # ...

What happens under the hood?:

# Assumed the view gets called like this
my_view(request, 'bla', 'blubb')

# At the end, the decorator would been called like this
can_do_foo('bla', 'blubb')

Passing queryset lookups to the permission

You can pass queryset lookups instead of an argument. This might look a bit strange first, but it can save you a ton of code. Instead of passing a simple string to the permission function, declare a tuple of the syntax:

(<model>, '<field_lookup>', 'view_arg')
# .. or ..
('<appname>.<modelname>', '<field_lookup>', 'view_arg')

Here is an example:

# permission.py
def can_do_foo(self, flatpage_instance=None):
    # ...

# views.py
from django.contrib.flatpages.models import Flatpage
@permission_required('flatpage_permission.can_do_foo', (Flatpage, 'url__iexact', 'url'))
def flatpage(required, url):
    # ...

What happens under the hood? It’s nearly the same as the simple decorator would do, except that the argument is fetched with a get_object_or_404 statement. So this is the same:

(Flatpage, 'url__iexact', 'url')
get_object_or_404(Flatpage, 'url__iexact'='/about/')

Note

For all available field lookups, please refer to the Django documentation: Field lookups

Contributed decorators

django-authority contributes two decorators, the syntax of both is the same as described above:

  • permission_required
  • permission_required_or_403

In a nutshell, permission_required_or_403 does the same as permission_required except it returns a Http403 Response instead of redirecting to the login page.

Just like Django’s 500.html and 404.html you are able to override the template used in the permission denied page. Simply create a 403.html template in your template directory. It will get the path of the denied page passed as the context variable request_path.