Antonius Common
Antonius Common

Reputation: 3031

Object Ownership in Django

I'm wondering how I might accomplish a simple 'object ownership' system with django models, such that, by default, only the owner of an object may edit it.

I am attempting to allow a 'Management' group to edit all objects on behalf of the object owners, and have at this point added a custom permission:

class Meta:
    permissions     = (
        ("manage_object", "Can manage objects"),
    )

To establish 'ownership' I've toyed with the idea of adding a def to the model:

def owner(self):
    return self.user

But then, how might I go further? I could implement the permissions in a view and display relevant UI with a template, i.e.:

if request.user is object.owner:
    # ... do stuff
elseif request.user.has_perm.can_manage:  # this line is probably not right
    # ... do something else

... and then present different UI elements on a template level.

So, the question is:

Best thanks!

Upvotes: 12

Views: 8750

Answers (3)

James Bennett
James Bennett

Reputation: 11163

A while back I wrote up the usual technique for doing this in the admin. You may want to read through that to see how the implementation works.

Upvotes: 2

Pyetras
Pyetras

Reputation: 1502

You can look into RowLevelPermissions branch. It hasn't been included even in 1.1 beta though, I guess it still needs some development.

Upvotes: 0

Steef
Steef

Reputation: 34715

My approach would be adding a method to the model:

class YourModelWithOwnership(models.model):
    ...

    def user_can_manage_me(self, user):
        return user == self.user or user.has_perm('your_app.manage_object')

I'd then call that method whenever a permission check is required, and take some action based on the outcome. So for a view that would be

from django.shortcuts import get_object_or_404
...

def view_func(request, item_id):
    item = get_object_or_404(YourModelWithOwnership, id=item_id) # or whatever is needed to get the object
    if not item.user_can_manage_me(request.user):
        # user not allowed to manage
        ...
    else:
        ...

Later I'd probably realize that that's still quite some boilerplate code to write in every view that needs that test, so I'd implement an exception that's thrown when a user can't manage an object...

class CannotManage(Exception):
    pass

...and add another method to the model:

from django.db import models
from django.shortcuts import get_object_or_404

class YourModelWithOwnership(models.model):
    ...

    @classmethod
    def get_manageable_object_or_404(cls, user, *args, **kwds):
        item = get_object_or_404(cls, *args, **kwds)
        if not item.user_can_manage_me(user):
            raise CannotManage
        return item

Then, in the view functions, this can be used:

def view_func(request, item_id):
    item = YourModelWithOwnership.get_manageable_object_or_404(request.user, id=item_id)
    ...

This will of course raise an exception when the user isn't the owner and does not have the proper permission. That exception can be handled in the process_exception() method of a custom middleware class so that there's a single handler for all instances where a user is not allowed to mess with the object.

Upvotes: 17

Related Questions