row level permissions in django row level permissions in django django django

row level permissions in django


For an application i'm building i want to provide row level permission through a simple decorator. I can do this because the condition is just whether the request.user is the owner of the model object.

Following seems to work:

from functools import wrapsfrom django.core.exceptions import PermissionDenied, ObjectDoesNotExistdef is_owner_permission_required(model, pk_name='pk'):    def decorator(view_func):        def wrap(request, *args, **kwargs):            pk = kwargs.get(pk_name, None)            if pk is None:                raise RuntimeError('decorator requires pk argument to be set (got {} instead)'.format(kwargs))            is_owner_func = getattr(model, 'is_owner', None)            if is_owner_func is None:                raise RuntimeError('decorator requires model {} to provide is_owner function)'.format(model))            o=model.objects.get(pk=pk) #raises ObjectDoesNotExist            if o.is_owner(request.user):                return view_func(request, *args, **kwargs)            else:                raise PermissionDenied        return wraps(view_func)(wrap)    return decorator

The view:

@login_required@is_owner_permission_required(Comment)def edit_comment(request, pk):    ...

Urls:

url(r'^comment/(?P<pk>\d+)/edit/$', 'edit_comment'),

The model:

class Comment(models.Model):    user = models.ForeignKey(User, ...    <...>    def is_owner(self, user):        return self.user == user

Any feedback or remarks are appreciated.

Paul Bormans


The plumbing is there (this is from the bottom of the same page you linked):

Handling object permissions

Django's permission framework has a foundation for object permissions, though there is no implementation for it in the core. That means that checking for object permissions will always return False or an empty list (depending on the check performed). An authentication backend will receive the keyword parameters obj and user_obj for each object related authorization method and can return the object level permission as appropriate.

But no default implementation is provided. Since this is a common topic; there are lots of answers on SO. Check to the right and you'll see some listed.

The basis idea is to browse the django packages' perm grid and pick an implementation of object level permissions. I personally like django-guardian.


The methods that the docs talk about will allow you to restrict access to particular objects in the admin. Each method is passed the object in play, which you can use to make determinations about whether a user can access it, by returning either True or False.

class MyModelAdmin(admin.ModelAdmin):    ...    def has_add_permission(self, request):        # This one doesn't get an object to play with, because there is no        # object yet, but you can still do things like:        return request.user.is_superuser        # This will allow only superusers to add new objects of this type    def has_change_permission(self, request, obj=None):        # Here you have the object, but this is only really useful if it has        # ownership info on it, such as a `user` FK        if obj is not None:            return request.user.is_superuser or \                   obj.user == request.user            # Now only the "owner" or a superuser will be able to edit this object        else:            # obj == None when you're on the changelist page, so returning `False`            # here will make the changelist page not even viewable, as a result,            # you'd want to do something like:            return request.user.is_superuser or \                   self.model._default_manager.filter(user=request.user).exists()            # Then, users must "own" *something* or be a superuser or they            # can't see the changelist    def has_delete_permission(self, request, obj=None):        # This pretty much works the same as `has_change_permission` only        # the obj == None condition here affects the ability to use the        # "delete selected" action on the changelist