more on this.

Now that we know how to trigger django permission creation mechanism with a fake migration, there is still one more issue. Those permissions are not associated to the right content_type (the one that corresponds to the proxy models), so they are useless in the django admin.

This is related to an old bug in django, by which , when creating a proxy model, the content type object associated to it was the same that the proxy model’s parent, but the admin permission system works using the app_name, so there is a contradiction in there.

On django 1.3 (I can’t tell for earlier versions), there is a new content type object created per each proxy model, but the automatic permissiion creation mechanism doesn’t work right with that. Django keeps creating permissions for the proxy model’s parent class, and the admin framework keeps needing more specific permissions to allow someone in.  This is related to the ContentTypeManager.get_for_model(class) method.

Well, there’s a hack I got from modifying this ticket attachment.:

Say you put all of your proxy models under an app called “customer”, so the “customers” can access a “special admon” in which you can customize several options without having to mutilate your own admon.

What we do is disconnect the default “create_permissions” function from the post_syncdb signal, connect it to our very own custom function, and, in case the “sender” of the signal is other than our particular app (which we know contains only proxy models), then use the old “create_permissions”funcion.

I put this in my customer/models.py

from django.db.models import get_models
from django.db.models.signals import post_syncdb
from django.utils.encoding import smart_unicode

from django.contrib.auth.management import create_permissions, _get_all_permissions

# Hack the postsyncdb signal, so we can fix the misbehavior of the
# content_type
# assignment to the proxy models.
# see http://code.djangoproject.com/ticket/11154

def create_permissions_respecting_proxy(
    app, created_models, verbosity, **kwargs
    ):

    if not kwargs['sender'].__name__ == 'customer.models':
        # if not in 'customer' app, then use the original function
        create_permissions(app, created_models, verbosity, **kwargs)
        return

    from django.contrib.contenttypes.models import ContentType
    from django.contrib.auth import models as auth_app
    app_models = get_models(app)
    searched_perms = list()
    ctypes = set()
    for klass in app_models:
        # this is where the difference is: the original create_permissions
        # use ctype = ContentType.objects.get_for_model(klass)
        opts = klass._meta
        ctype, created = ContentType.objects.get_or_create(
            app_label=opts.app_label,
            model=opts.object_name.lower(),
            defaults = {'name': smart_unicode(opts.verbose_name_raw)}
            )
        # end of the modification
        ctypes.add(ctype)
        for perm in _get_all_permissions(klass._meta):
            searched_perms.append((ctype, perm))

    all_perms = set(auth_app.Permission.objects.filter(
            content_type__in=ctypes
            ).values_list("content_type", "codename"))

    for ctype, (codename, name) in searched_perms:
        if(ctype.pk, codename) in all_perms:
            continue
        p = auth_app.Permission.objects.create(
            codename=codename, name=name, content_type=ctype
            )
        if verbosity >=2:
            print "Adding permission '%s'" % p

post_syncdb.disconnect(
    create_permissions,
    dispatch_uid='django.contrib.auth.management.create_permissions',
    )

post_syncdb.connect(
    create_permissions_respecting_proxy,
    dispatch_uid='django.contrib.auth.management.create_permissions',
    )

enjoy.


One Comment on “django: setting the right permissions associated with proxy models”

You can track this conversation through its atom feed.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>