[PYTHON] Limit Views using Django's Permission model

Django has a model called Permission, which is automatically generated when you create a Django model. As a result, on the management screen, only users with the corresponding authority can create, update, and delete table data. This time I would like to use this Permission to limit the view.

Create your own Permission

Let's create a new one without using the existing Permission data.

Specifying ContentType

The Permission model requires you to specify ContentType as a foreign key, so there must be a corresponding ContentType. I think you can specify the ContentType of the application that has the View you are trying to limit, but you may want to create your own.

from django.contrib.contenttypes.models import ContentType


ContentType.objects.create(
    app_label='app_label', name='name', model='model')

The application name is specified as ʻapp_label. In other words, it is the end of the module name specified in settings such as ʻINSTALLED_APPS.

Creating Permission data

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


content_type = ContentType.objects.get(
    app_label='app_label', name='name', model='model')

Permission.objects.create(
    content_type_id=content_type.id, name='name', codename='codename')

name is the display name of the permission, such as'Can add permission', and codename is the distinguished name, such as'add_permission'.

Grant Permission to user

You can grant permissions from the user edit screen on the Django admin screen.

perm.png

Find out if a user has a particular Permission

You can check if you have a specific Permission by calling has_perm of the ʻUserinstance. Pass it as an argument in the form'app_label.codename'. For example, the permission to add a user is 'auth.add_user'`.

from django.contrib.auth import get_user_model


User = get_user_model()
user = User.objects.get(pk=1)

user.has_perm('{app_label}.{codename}'.format(
    app_label='app_label', codename='codename'))

Limit View with decorator

(Additional notes)

As a reference, Django has a way to use a decorator to create a View that only staff users can see.

from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
from django.views.generic.base import View


class SpamView(View):
    @method_decorator(staff_member_required)
    def dispatch(self, *args, **kwargs):
        return super(SpamView, self).dispatch(*args, **kwargs)

By imitating this, we will create a View that can only be viewed by users with specific permissions.

decorators.py


from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test


def get_permission_deco(permission_codename,
                        redirect_field_name=REDIRECT_FIELD_NAME,
                        login_url='admin:login'):
    def deco(view_func):
        return user_passes_test(
            lambda u: u.has_perm(permission_codename),
            login_url=login_url,
            redirect_field_name=redirect_field_name
        )(view_func)
    return deco

view.py


from django.utils.decorators import method_decorator
from django.views.generic.base import View

from .decorators import get_permission_deco


class EggView(View):
    @method_decorator(get_permission_deco('app_label.codename'))
    def dispatch(self, *args, **kwargs):
        return super(EggView, self).dispatch(*args, **kwargs)

(Addition)

There was a permission_required in Django without any tricks. https://docs.djangoproject.com/ja/1.10/topics/auth/default/#the-permission-required-decorator

Limit templates with custom tags

The details of how to create a custom tag are beyond the scope of this article, so I will omit it, but I think that you may limit the template by using a custom tag, so I will give an example.

perm_extra.py


from django import template


register = template.Library()

@register.filter(name='has_perm')
def has_perm(user, permission_name):
    return user.has_perm(permission_name)

{% load perm_extra %}

{% if user|has_perm:'app_label.codename' %}
  <a href="/path">Go to edit page</a>
{% else %}
  <del>Go to edit page</del>(No authority)
{% endif %}

Recommended Posts

Limit Views using Django's Permission model
Benefits of using slugfield in Django's model
Django's Model Form was amazing.
Benefits of refining Django's Model
Try using Django's template feature