How to dynamically name permissions in a Django abstract model class?

2.6k Views Asked by At

I want to define some custom permissions on an abstract model class that would then be inherited by all child classes, and rather than give the permissions a generic object name that could apply to any subclassed model type, I would like to essentially use the verbose_name_plural property of the child model as part of the permission names and description (e.g. ('view_classname', 'Can view classname')), emulating Django's default behavior.

So, what I would be hoping to do would be something like this (which doesn't work, since verbose_name_plural is not defined in this context):

class AbstractModel(models.Model):
    class Meta:
        abstract = True
        permissions = (
            (u'view_%ss' % verbose_name_plural, u'Can view %s' % verbose_name_plural),
        )

(This problem is also described at http://code.djangoproject.com/ticket/10686, which includes a patch that implements dynamic replacement of %(class)s in permission definitions, but this patch was never accepted and my production environment does not allow for patching Django.)

3

There are 3 best solutions below

0
On

Could you accomplish this with a class decorator instead of an abstract model class?

def with_view_perm(cls):
    vn = cls.Meta.verbose_name_plural
    perms = (('view_%s' % vn, 'Can view %s' % vn),)
    cls.Meta.perms += perms
    return cls

@with_view_perm
class Child(models.Model):
    class Meta:
        verbose_name_plural = 'children'
        perms = (('change_children', 'Can change children'),)
2
On

It's old - but for future reference - the desired behaviour is working out of the box now (Django 1.9)

Consider this abstract model with the appropriate permissions:

class DetailContentLifecycleClassModel (models.Model):
    class Meta:
        abstract=True
        permissions = (
            ('can_change_content', 'Change content of the model'),
            ('can_submit_for_approval', 'Ask for final check and publishing'),
            ('can_publish_content', 'Publish the model as a new version'),
        )

When inheriting like this:

class Test_Details (DetailContentLifecycleClassModel):
    name = models.CharField(max_length=200)

class Test_Details2 (DetailContentLifecycleClassModel):
    name = models.CharField(max_length=200)

The Permssions are created as following:

from playground.models import Test_Details
from django.contrib.auth.models import User, Permission

tmp = Permission.objects.filter()

Result (which is exactly what was wanted):

playground | test_ details | Can add test_ details
playground | test_ details | Change content of the model
playground | test_ details | Publish the model as a new version
playground | test_ details | Ask for final check and publishing
playground | test_ details | Can change test_ details
playground | test_ details | Can delete test_ details
playground | test_ details2 | Can add test_ details2
playground | test_ details2 | Change content of the model
playground | test_ details2 | Publish the model as a new version
playground | test_ details2 | Ask for final check and publishing
playground | test_ details2 | Can change test_ details2
playground | test_ details2 | Can delete test_ details2
0
On

The other newer way of doing this is to set default_permissions in the Meta on your base class.

Also be aware that when doing that you to make and run migrations for it to take effect.