3

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.)

gravelpot
  • 1,677
  • 1
  • 14
  • 20
  • This is still a problem, but the vaguely positive news for more recent comers to this is that the issue received some work on it 6 months ago, but hasn't made it into a release yet (and we're at 1.10.5 at time of writing). https://github.com/django/django/pull/6861#issuecomment-240427426 – Carl Marshall Feb 14 '17 at 13:01

3 Answers3

0

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
Wolfgang
  • 66
  • 5
  • Not quite the case. Investigating the contents of `auth_permission` will show the `codename` to still be the original `can_change_content` for each content type, rather than `can_change_content_details2`, for example. Your own example just shows the list of apps, classes, and the description text (which also would benefit from having the class context name in). Am currently trying to solve this same issue myself. – Carl Marshall Feb 14 '17 at 12:45
  • @CarlMarshall I believe I just accomplished this by using [Default permissions.](https://docs.djangoproject.com/en/1.10/ref/models/options/#default-permissions) This creates permissions.codenames correctly, however I do not seem to have any control over permission.name, but I don't really care too much. – DrS Mar 30 '17 at 14:35
0

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.

Gordon Wrigley
  • 11,015
  • 10
  • 48
  • 62
0

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'),)
dgel
  • 16,352
  • 8
  • 58
  • 75