Permissions¶
Permissions are handled on a per view basis. So basically each admin view can 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.
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:
from django.views import generic
from djadmin2.viewmixins import Admin2Mixin
from djadmin2 import permissions
class MyView(Admin2Mixin, generic.TemplateView):
permission_classes = (
permissions.IsStaffPermission,
permissions.ModelViewPermission)
See the following sections on which permission classes ship with django-admin2, ready to use and how you can roll your own.
Built-in permission classes¶
You can use the following permission classes directly in you views.
-
class
djadmin2.permissions.
IsStaffPermission
¶ It ensures that the user is authenticated and is a staff member.
-
class
djadmin2.permissions.
IsSuperuserPermission
¶ It ensures that the user is authenticated and is a superuser. However it does not check if the user is a staff member.
-
class
djadmin2.permissions.
ModelViewPermission
¶ Checks if the user has the
<app>.view_<model>
permission.
-
class
djadmin2.permissions.
ModelAddPermission
¶ Checks if the user has the
<app>.add_<model>
permission.
-
class
djadmin2.permissions.
ModelChangePermission
¶ Checks if the user has the
<app>.change_<model>
permission.
-
class
djadmin2.permissions.
ModelDeletePermission
¶ Checks if the user has the
<app>.delete_<model>
permission.
Writing your own permission class¶
If you need it, writing your own permission class is really easy. You just need
to subclass the djadmin2.permissions.BasePermission
class and
overwrite the has_permission()
method that implements the desired permission checking. The arguments that the
method takes are pretty self explanatory:
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:
from djadmin2.permissions import BasePermission
class HasAccessToSecretInformationPermission(BasePermission):
'''
Only allow superusers access to secret information.
'''
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¶
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.
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:
{% if permissions.has_change_permission %}
<a href="... link to change form ...">Edit {{ object }}</a>
{% endif %}
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:
{% load admin2_tags %}
{% if permissions.has_change_permission|for_object:object %}
<a href="... link to change form ...">Edit {{ object }}</a>
{% endif %}
Note
Please be aware, that the 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.
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:
{% load admin2_tags %}
{% with permissions|for_object:object as object_permissions %}
{% if object_permissions.has_change_permission %}
<a href="... link to change form ...">Edit {{ object }}</a>
{% endif %}
{% if object_permissions.has_delete_permission %}
<a href="... link to delete page ...">Delete {{ object }}</a>
{% 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:
{% 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¶
Sometimes you just need to check the permissions for that particular model. In that case, you can access its permissions like this:
{% if permissions.blog_post.has_view_permission %}
<a href="...">View {{ post }}</a>
{% endif %}
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
. 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:
{% 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.
{% load admin2_tags %}
{% with "add" as view_name %}
{% if permissions|for_view:view_name %}
<a href="...">{{ view_name|capfirst }} model</a>
{% 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
.