Howdy!

I've banged together a rudimentary row-level permissions framework that
looks something like...

from django.db import models
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class ObjectPermission(models.Model):
    model_id = models.IntegerField("ID of the object")
    model_ct = models.ForeignKey(ContentType,
                                 verbose_name="Object content model",
                                 related_name="model_ct")
    permission = models.ForeignKey(Permission)

    obj = generic.GenericForeignKey('model_ct', 'model_id')

class UserACL(models.Model):
    user = models.ForeignKey(User)
    permissions = models.ManyToManyField(ObjectPermission)

class Widget(models.Model):
    name = models.CharField(maxlength=50)
    object_permissions = generic.GenericRelation(ObjectPermission,
                                                 object_id_field='model_id',

content_type_field='model_ct')

    class Meta:
        permissions = [('can_juggle_knives', 'Knife Juggler'),
                       ('can_dodge_bullets', 'Bullet Dodget'),
                       ('can_swallow_razorblades', 'Razor Swallower'),
                       ('can_do_own_taxes', 'Tax Preparer')]


I wanted to use the db-api to get all of the widgets for which a certain
user had a certain permission. But in my early experimentation on developing
this call, I run into some unexpected results.

$ ./manage.py shell
Python 2.5.1 (r251:54863, Oct  5 2007, 13:50:07)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from myapp.models import *
>>> jag = User.objects.get()
>>> ct = ContentType.objects.get_for_model(Widget)
>>> perms = Permission.objects.filter(codename__startswith='can_')
>>> widget = Widget.objects.create(name='FooWidget')
>>> widget.object_permissions.all()
[]
>>> op = ObjectPermission(model_ct=ct, model_id=widget.id,
permission=perms[0])
>>> op.save()
>>> acl = UserACL(user=jag)
>>> acl.save()
>>> acl.permissions.add(op)

To confirm the setups worked...

>>> widget.object_permissions.all()
[<ObjectPermission: ObjectPermission object>]
>>> acl.permissions.all()
[<ObjectPermission: ObjectPermission object>]
>>> acl.permissions.all()[0].obj
<Widget: Widget object>
>>> Widget.objects.filter(object_permissions__id__isnull=False)
[<Widget: Widget object>]

Looks great so far. Now I add another object permission...

>>> op2 = ObjectPermission(model_ct=ct, model_id=widget.id,
permission=perms[1])
>>> op2.save()
>>> Widget.objects.filter(object_permissions__id__isnull=False)
[<Widget: Widget object>, <Widget: Widget object>]

That part's right. This next part isn't:

>>> Widget.objects.filter
(object_permissions__permission__codename=perms[0].codename)
[<Widget: Widget object>, <Widget: Widget object>]
>>> perms[0].codename != perms[1].codename
True

What? There should have only been one Widget in the queryset.

>>> Widget.objects.filter
(object_permissions__permission__codename=perms[0].codename)._get_sql_clause()
(['"myapp_widget"."id"', '"myapp_widget"."name"'], ' FROM "myapp_widget"
LEFT OUTER JOIN "myapp_objectpermission" AS
"m2m_myapp_widget__object_permissions" ON "myapp_widget"."id" =
"m2m_myapp_widget__object_permissions"."model_id" INNER JOIN
"myapp_objectpermission" AS "myapp_widget__object_permissions" ON
"m2m_myapp_widget__object_permissions"."model_id" =
"myapp_widget__object_permissions"."id" INNER JOIN "auth_permission" AS
"myapp_widget__object_permissions__permission" ON
"myapp_widget__object_permissions"."permission_id" =
"myapp_widget__object_permissions__permission"."id" WHERE
("myapp_widget__object_permissions__permission"."codename" = %s)',
[u'can_dodge_bullets'])


So it looks like we're joining to object_permissions twice and only
constraining that join to instances of the correct permission codename on
one of those two joins, resulting in one Widget object for every object
permission there is. That doesn't sound right.

This looks like the issue was discussed briefly here: http://lnk4.us/WNuQ
... but it doesn't look like it was ever resolved. Can anybody give me some
pointers on where to look to begin solving this issue? I'd love to be able
to run my query without having to resort to raw SQL. Thanks!

-jag

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to