Thursday, April 23, 2009

Django Custom Permissions ... sorta

There is no built in way to do row level permissions in Django, yet (see here) I have found a few hacks to get around this in the admin section. This is using 1.0.2 First add a Foreign key to the model this is an example models.py
from django.contrib.auth.models import User, Group
from django.db import models

class MyModel(models.Model):
   name = models.CharField(max_lenght=100)
   group = models.ForeignKey(Group)
Then in admin.py we need to override the queryset so that the user can only see objects that are part of his group and that he can only add objects that belong to his group
from django.contrib.auth.models import Group
from django.contrib import admin
from myproject.myapp.models import MyModel

class MyModelAdmin(admin.ModelAdmin):
   def __call__(self,request,url):
       self.request = request
       return super(DeviceAdmin, self).__call__(request,url)
   def formfield_for_dbfield(self, db_field, **kwargs):
       field = super(DeviceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
       if not self.request.user.is_superuser and db_field.name == 'group':
           my_choices = [('', '---------')]
           my_choices.extend(
               Group.objects.filter(
                   name__in=self.request.user.groups.all()
               ).values_list('id','name')
           ) # This can be one line it just doesn't fit
           print my_choices
           field.choices = my_choices
       return field
    def queryset(self, request):
        qs = super(MyModelAdmin, self).queryset(request)
        if request.user.is_superuser:
            return qs
        else:
            group_qs = Group.objects.filter(name__in=request.user.groups.all())
            return qs.filter(group__in=group_qs)

admin.site.register(MyModel,MyModelAdmin)
Now if you setup the admin in your settings and urls the groups for MyModel will only show up if the user belongs to that group. If the user is a super user they will see all the groups. If they user does not belong to any groups then they will not see any groups. Note: You can probably submit a post with the ID of a group that you do not belong to and it will work, I have not figured out how to add custom validation based on the request object yet. Here are some links that I used as a reference: http://stackoverflow.com/questions/430592/djang-admin-charfield-as-textarea http://www.djangosnippets.org/snippets/414/ http://code.djangoproject.com/ticket/3987#comment:32