DjangoのPermissionモデルを利用してViewを制限する

  • 19
    Like
  • 1
    Comment

DjangoにはPermissionというモデルがあり、Djangoのmodelを作成すると自動で生成されます。それによって管理画面では対応する権限を持つユーザーのみがテーブルデータを作成・更新・削除できるといった制限が可能です。今回はこのPermissionを使ってViewの制限をしたいと思います。

独自のPermissionを作る

既存のPermissionデータを使わずに新しく作ってみます。

ContentTypeの指定

Permissionモデルは外部キーとしてContentTypeを指定する必要があるので、対応するContentTypeが無くてはなりません。制限しようとしているViewのあるアプリケーションのContentTypeを指定すればいいと思いますが、独自のものを作ってもいいかもしれません。

from django.contrib.contenttypes.models import ContentType


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

app_labelとして指定しているのはアプリケーション名。つまりsettingsのINSTALLED_APPSなどに指定するモジュール名の末尾です。

Permissionデータの作成

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'Can add permission'などパーミッションの表示名で、codename'add_permission'など、識別名です。

ユーザーにPermissionを付与

Djangoの管理画面のユーザー編集画面から権限の付与ができます。

perm.png

ユーザーが特定のPermissionを持っているか調べる

Userインスタンスのhas_permを呼べば特定のPermissionを持っているか確認できます。引数に渡すのは'app_label.codename'という形になります。たとえばユーザー追加のパーミッションは'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'))

デコレーターでViewを制限

(追記あり)

参考として、Djangoではスタッフユーザーだけが見る事ができるViewを作る場合、デコレータを利用する方法があります。

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)

これをまねて特定のパーミッションを持つユーザーだけが見る事ができるViewを作ります。

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)

(追記)

小細工しなくてもDjangoにはpermission_requiredというのがありました。
https://docs.djangoproject.com/ja/1.10/topics/auth/default/#the-permission-required-decorator

カスタムタグでテンプレートを制限

カスタムタグの作り方の詳細はこの記事の範囲を超えますので省略しますが、カスタムタグを使ってテンプレートを制限する事もあるかと思うので一例を載せておきます。

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">編集ページへ</a>
{% else %}
  <del>編集ページへ</del>(権限がありません)
{% endif %}