I have some troubles understanding how Django 3.1 checks permissions, especially how model permissions versus default permissions are handled.
I have the following model:
from django.db import models
class MyModel(models.Model):
class Meta:
permissions = [("finalize", "Can finalize"),]
is_final = models.BooleanField(default = False)
which defines a new finalize permission for this model. With the model along we have a model admin:
from django.contrib import admin
from django.contrib.auth import get_permission_codename
from .models import *
class MyModelAdmin(admin.ModelAdmin):
actions = ['set_final', 'remove_final']
def set_final(self, request, queryset):
queryset.update(is_final=True)
set_final.allowed_permissions = ('finalize',)
def remove_final(self, request, queryset):
queryset.update(is_final=False)
remove_final.allowed_permissions = ('finalize',)
# Permissions
def has_change_permission(self, request, obj = None):
if obj and obj.is_final:
return False
codename = get_permission_codename('change', self.opts)
print("has_perm(change) : %s.%s : %s" % (self.opts.app_label, 'change', request.user.has_perm('%s.%s' % (self.opts.app_label, 'change'))))
print("has_perm(codename): %s.%s : %s" % (self.opts.app_label, codename, request.user.has_perm('%s.%s' % (self.opts.app_label, codename))))
return request.user.has_perm('%s.%s' % (self.opts.app_label, codename))
def has_finalize_permission(self, request, obj = None):
codename = get_permission_codename('finalize', self.opts)
print("has_perm(finalize): %s.%s : %s" % (self.opts.app_label, 'finalize', request.user.has_perm('%s.%s' % (self.opts.app_label, 'finalize'))))
print("has_perm(codename): %s.%s : %s" % (self.opts.app_label, codename, request.user.has_perm('%s.%s' % (self.opts.app_label, codename))))
return request.user.has_perm('%s.%s' % (self.opts.app_label, 'finalize'))
admin.site.register(MyModel, MyModelAdmin)
which defines two actions: setting and removing the is_final flag from objects. Along with it, the has_change_permission is redefined because the object should be displayed as "view"-only if the is_final flag is set.
Now I create a new staff-level user which receives all permissions for the model (view, add, change, finalize) via the Django admin interface. And here comes the conundrum. When logging in with that user, the above print statements evaluate to the following:
has_perm(finalize): myapp.finalize : True
has_perm(codename): myapp.finalize_mymodel : False
has_perm(change) : myapp.change : False
has_perm(codename): myapp.change_mymodel : True
(bear in mind that the user has received all permissions on the model selectable from the list in the user admin interface). So somehow
- calling the model-defined permission with the value from
get_permission_codename()evaluates tofalse - calling the default permission with its direct name
myapp.changeevaluates tofalse
Could someone explain to me why this behavior is expected and how to properperly implement checking for permissions?