.. _check-decorator:

=====================================
Check permissions using the decorator
=====================================

.. index::
    single: permission_required
    single: permission_required_or_403

.. note:: A decorator is not the ultimate painkiller, if you need to deal with
    complex permission handling, take a look at :ref:`check-python`.

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`_

.. _Field lookups: http://docs.djangoproject.com/en/dev/ref/models/querysets/#id7

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``.
