はじめに
django.contrib.admin.ModelAdmin
を作る時、
has_change_permission(request, obj=None)
-
has_delete_permission(request, obj=None)
上記2つのフックメソッドをオーバーライドすることで、ログインユーザやインスタンスに対して権限を設定することが可能です。
ですが、上記のフックはviewのfunctionとは違い、admin上で呼び出される個所が複数あるため、少し注意が必要です。
Environments
- Python 3.6
- Django 1.11
has_change_permission(request, obj=None)
このフックでobj
にインスタンスが入ってるのは、インスタンスの変更フォームである、
/admin/{{ app_label }}/{{ model_name }}/{{ object_id }}/change/
のみです。
これ以外
-
/admin/
: 管理ページトップ -
/admin/{{ app_label }}/
: appトップ -
/admin/{{ app_label }}/{{ model_name }}/
: Modelのchangelist
上記3ヶ所で、obj=None
として呼び出されますので、
admin.py
def has_change_permission(self, request, obj=None):
has_perm = super().has_change_permission(request, obj)
if isinstance(obj, self.model):
return obj.user == request.user and has_perm
return has_perm
こういう風に分岐が必要になります。
has_delete_permission(request, obj=None)
obj
に関してはchange
と同じですが、もう一つ。
Admin actionsとしてビルトインされてるdelete_selected
からこのフックが呼ばれる際、削除しようとする個々のインスタンスに対する権限チェックをしないため、常にobj=None
になるので
admin.py
has_perm = super().has_delete_permission(request, obj)
if request.POST.get('action') == 'delete_selected':
selected_qs = self.model.objects.filter(pk__in=request.POST.getlist('_selected_action'))
return all([o.user == request.user for o in selected_qs]) and has_perm
のような処理が必要です。
最後に
そもそも権限がないインスタンスはget_queryset(request)
でリストから出さないようにしましょう。
References
Django Admin: has_delete_permission Ignored for “Delete” Action