.. _create-per-object-permission:

==============================
Create a per-object permission
==============================

django-authority provides a super simple but nifty feature called *per-object
permission*. A description would be::

    Attach a <codename> to an object
    Attach a <codename> to an user

    If the user has <codename> and the object has <codename> then do-something,
    otherwise do-something-else.

This might sound strange but let's have a closer look on this pattern.
In terms of users and flatpages a visual example would be:

.. image:: .static/authority-object-1to1.png

*The user is allowed to review the flatpage "Events".*

You are not limited to a 1:1 relation, you can add this ``codename`` to
multiple objects:

.. image:: .static/authority-object-1toN.png

*The user is allowed to review the flatpages "Events" and "Contact".*

And you can do this with any objects in any direction:

.. image:: .static/authority-object-NtoN.png

*The user is allowed to review the flatpages "Events" and "Contact". Another
user is allowed to publish the flatpage "Events".*

Create per-object permissions
=============================

Creating per-object permissions is super simple. See this piece of permission
class code::

    class FlatPagePermission(BasePermission):
        label = 'flatpage_permission'
        checks = ('review',)

    authority.register(FlatPage, FlatPagePermission)

This permission class is similar to the one we already created in
:ref:`create-basic-permission` but we added the line::

    checks = ('review',)

This tells the permission class that it has a permission check (or ``codename``)
``review``. Under the hood this check gets translated to ``review_flatpage``
(``review_<modelname>``).

.. important:: Be sure that you have understand that we have not written any
   line of code yet. We just added the ``codename`` to the checks attribute.

Attach per-object permissions to objects
========================================

Please see :ref:`handling-admin` for this.

Check per-object permissions
============================

As we noted above, we have not written any permission comparing code yet. This
is your work. In theory the permission lookup for per-object permissions is::

    if <theuser> has <codename> and <object> has <codename>:
        return True
    else:
        return False

.. important::

    The syntax is similiar to the permission checks we've already
    seen in :ref:`create-basic-permission` for the basic permissions but now
    we have to pass each function a model instance we want to check!

In your python code
-------------------
::

    from myapp.permissions import FlatPagePermission
    def my_view(request):
        check = FlatPagePermission(request.user)
        flatpage_object = Flatpage.objects.get(url='/homepage/')
        if check.review_flatpage(flatpage_object):
            print "Yay, you can change *this* flatpage!"

Using the view decorator
------------------------
::

    from django.contrib.auth import Flatpage
    from authority.decorators import permission_required_or_403

    @permission_required_or_403('flatpage_permission.review_flatpage',
                                (Flatpage, 'url__iexact', 'url')) # The flatpage_object
    def my_view(request, url):
        # ...

See :ref:`check-decorator` how the decorator works in detail.

In your templates
-----------------
::

    {% ifhasperm "flatpage_permission.review_flatpage" request.user flatpage_object %}
        Yay, you can change *this* flatpage!
    {% else %}
        Nope, sorry. You aren't allowed to change *this* flatpage.
    {% endifhasperm %}

See :ref:`check-templates` how the template tag works in detail.
