diff --git a/docs/reference.rst b/docs/reference.rst
index 7394dd7..7d994a1 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -37,12 +37,16 @@ Permissions
===========
Permissions are handled on a per view basis. So basically each admin view can
-hold its own permission handling. That way you are very flexible in defining
-who is allowed to access your edit view without limitting yourself to simple
-model based permissions (like ``user.has_perm('blog.change_post')``.
+hold its own permissions. That way you are very flexible in defining
+who is allowed to access which view. For example, the edit view might need some
+totally different permission checks then the delete view. However the add view
+has nearly the same requirements as the edit view, you just also need to have
+on extra permission. All those scenarios can be handled very easily in **django-admin2**.
-You can attach a permission backend to a view by assigning a list of those to
-the ``permission_classes`` attribute:
+Since the permission handling is centered around the specific views, this is
+the place where you attach the permission checking logic to. You can assign one
+or more permission backends to a view by setting the ``permission_classes``
+attribute:
.. code-block:: python
@@ -57,10 +61,12 @@ the ``permission_classes`` attribute:
permissions.ModelViewPermission)
See the following sections on which permission classes ship with
-django-admin2, ready to use and how you can roll your own.
+**django-admin2**, ready to use and how you can roll your own.
-Builtin permission backends
----------------------------
+Builtin permission classes
+--------------------------
+
+You can use the following permission classes directly in you views.
.. autoclass:: djadmin2.permissions.IsStaffPermission
@@ -72,61 +78,61 @@ Builtin permission backends
.. autoclass:: djadmin2.permissions.ModelDeletePermission
-Writing your own permission backend
------------------------------------
+Writing your own permission class
+---------------------------------
-Internally a permission class uses so called *permission checks* to implement
-the real logic of verifying that a user has the correct permissions. A
-permission check has the real simple interface of accepting two position
-arguments and an optional third one. The first is the current ``request``,
-the second the ``view`` on which the permission check is performed against.
-The third and optional one is an arbitrary that can be passed into the
-permission checking machinery to implement object level permissions. Based on
-these arguments should the permission check than return either ``True`` if the
-permission shall be granted. A returned ``False`` means that the permission
-check failed and access to the user shall be denied.
+If you need it, writing your own permission class is really easy. You just need
+to subclass the :class:`djadmin2.permissions.BasePermission` class and
+overwrite the :meth:`~djadmin2.permissions.BasePermission.has_permission`
+method that implements the desired permission checking. The arguments that the
+method takes are pretty self explanatory:
-Here is an example implementation of a custom permission check:
+``request``
+ That is the request object that was sent to the server to access the
+ current page. This will usually have the ``request.user`` attribute which
+ you can use to check for user based permissions.
+
+``view``
+ The ``view`` argument is the instance of the class based view that the user wants
+ to access.
+
+``obj``
+ This argument is optional and will only be given if an object-level
+ permission check is performed. Take this into account if you want to
+ support object-level permissions, or ignore it otherwise.
+
+Based on these arguments should the ``has_permission`` method than return
+either ``True`` if the permission shall be granted or ``False`` if the access
+to the user shall be diened.
+
+Here is an example implementation of a custom permission class:
.. code-block:: python
- def secret_information_check(request, view, obj=None):
+ from djadmin2.permissions import BasePermission
+
+ class HasAccessToSecretInformationPermission(BasePermission):
'''
Only allow superusers access to secret information.
'''
- if 'secret' in obj.title.lower() and not request.user.is_superuser:
- return False
- return True
-You can use the following predefined permission checks or built your own:
-
-.. autofunction:: djadmin2.permissions.is_authenticated
-
-.. autofunction:: djadmin2.permissions.is_staff
-
-.. autofunction:: djadmin2.permissions.is_superuser
-
-.. autofunction:: djadmin2.permissions.model_permission
-
-You can now build your own permission class by subclassing
-``djadmin2.permissions.BasePermission`` and assigning a list of those
-permission checks to the ``permissions`` attribute:
-
-.. code-block:: python
-
- class SecretContentPermission(permissions.BasePermission):
- permissions = (
- permissions.is_staff,
- secret_information_check)
+ def has_permission(self, request, view, obj=None):
+ if obj is not None:
+ if 'secret' in obj.title.lower() and not request.user.is_superuser:
+ return False
+ return True
Permissions in templates
------------------------
-There is a ``{{ permissions }}`` variable available in the admin templates to
-provide easy checking if the user has valid permission for a specific view.
+Since the permission handling is designed around views, the permission checks
+in the template will also always access a view and return either ``True`` or
+``False`` if the user has access to the given view. There is a ``{{ permissions
+}}`` variable available in the admin templates to perform these tests against a
+specific view.
-You can check for either view, add, change and delete permissions. To do so you
-use the provided ``permissions`` variable as seen below:
+At the moment you can check for view, add, change and delete permissions. To do
+so you use the provided ``permissions`` variable as seen below:
.. code-block:: html+django
@@ -134,10 +140,33 @@ use the provided ``permissions`` variable as seen below:
Edit {{ object }}
{% endif %}
-This will check for the particular model that the current view is working with,
-if the user has the permission to access the change view. You can also use some
- object level permissions if you want to. For this just use the
-``for_object`` filter implemented in the ``admin2_tags`` templatetag library:
+This permission check will use the ``ModelAdmin2`` instance of the current view
+that was used to render the above template to find the view it should perform
+the permission check against. Since we test the change permission, it will use
+the ``update_view`` to check if the user has the permission to access the
+change page or not. If that's the case, we can safely display the link to
+the change page.
+
+At the moment we can check for the following four basic permissions:
+
+``has_view_permission``
+ This will check the permissions against the current admin's ``detail_view``.
+
+``has_add_permission``
+ This will check the permissions against the current admin's ``create_view``.
+
+``has_change_permission``
+ This will check the permissions against the current admin's ``update_view``.
+
+``has_delete_permission``
+ This will check the permissions against the current admin's ``delete_view``.
+
+Object-level permissions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The permission handling in templates also support checking for object-level
+permissions. To do so, you can use the ``for_object`` filter implemented in the
+``admin2_tags`` templatetag library:
.. code-block:: html+django
@@ -148,31 +177,42 @@ if the user has the permission to access the change view. You can also use some
{% endif %}
.. note::
+
Please be aware, that the :class:`django.contrib.auth.backends.ModelBackend`
backend that ships with django and is used by default doesn't support object
level permission. So unless you have implemented your own permission backend
that supports it, the
``{{ permissions.has_change_permission|for_object:object }}`` will always
return ``False`` and though will be useless.
-
-The following permission checks are currently supported:
+Sometimes you have the need to perform all the permission checks in a block of
+template code to use one object. In that case you can *bind* an object to the
+permissions variable for easier handling:
-``has_view_permission``
- Checks if the user has the permission to access the ``detail_view`` view
- from the current ``ModelAdmin2`` object.
-
-``has_add_permission``
- Checks if the user has the permission to access the ``create_view`` view
- from the current ``ModelAdmin2`` object.
+.. code-block:: html+django
-``has_change_permission``
- Checks if the user has the permission to access the ``update_view`` view
- from the current ``ModelAdmin2`` object.
+ {% load admin2_tags %}
-``has_delete_permission``
- Checks if the user has the permission to access the ``delete_view`` view
- from the current ``ModelAdmin2`` object.
+ {% with permissions|for_object:object as object_permissions %}
+ {% if object_permissions.has_change_permission %}
+ Edit {{ object }}
+ {% endif %}
+ {% if object_permissions.has_delete_permission %}
+ Delete {{ object }}
+ {% endif %}
+ {% endwith %}
+
+That also comes in handy if you have a rather generic template that performs
+some permission checks and you want it to use object-level
+permissions as well:
+
+.. code-block:: html+django
+
+ {% load admin2_tags %}
+
+ {% with permissions|for_object:object as object_permissions %}
+ {% include "list_of_model_actions.html" with permissions=object_permissions %}
+ {% endwith %}
Checking for permissions on other models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -188,6 +228,43 @@ that case, you can access its permissions like this:
So what we actually did here is that we just put the name of the
``ModelAdmin2`` that is used for the model you want to access between the
-``permissions`` variable and the ``has_view_permission`` permission check. This
-name will be the app label followed by the model name in lowercase with an
-underscore in between for ordinary django models.
+``permissions`` variable and the ``has_view_permission``. This name will be the
+app label followed by the model name in lowercase with an underscore in between
+for ordinary django models. That way you can break free of beeing limitted to
+permission checks for the current ``ModelAdmin2``. But that doesn't help you
+either if you don't know from the beginning on which model admin you want to
+check the permissions. Imagine the admin's index page that should show a list
+of all the available admin pages. To dynamically bind the permissions variable
+to a model admin, you can use the ``for_admin`` filter:
+
+.. code-block:: html+django
+
+ {% load admin2_tags %}
+
+ {% for admin in list_of_model_admins %}
+ {% with permissions|for_admin:admin as permissions %}
+ {% if permissions.has_add_permission %}Add another {{ admin.model_name }}{% endif %}
+ {% endwith %}
+ {% endfor %}
+
+Dynamically check for a specific permission name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Just like you can bind a permission dynamically to a model admin, you can also
+specify the actual permission name on the fly. There is the ``for_view`` filter
+to do so.
+
+.. code-block:: html+django
+
+ {% load admin2_tags %}
+
+ {% with "add" as view_name %}
+ {% if permissions|for_view:view_name %}
+ {{ view_name|capfirst }} model
+ {% endif %}
+ {% endwith %}
+
+That way you can avoid hardcoding the ``has_add_permission`` check and make the
+checking depended on a given template variable. The argument for the
+``for_view`` filter must be one of the four strings: ``view``, ``add``,
+``change`` or ``delete``.