Search code examples
djangodjango-modelsdjango-managersdjango-custom-manager

Django custom managers - how do I return only objects created by the logged-in user?


I want to overwrite the custom objects model manager to only return objects a specific user created. Admin users should still return all objects using the objects model manager.

Now I have found an approach that could work. They propose to create your own middleware looking like this:

#### myproject/middleware/threadlocals.py

try:
    from threading import local
except ImportError:
    # Python 2.3 compatibility
    from django.utils._threading_local import local

_thread_locals = local()

def get_current_user():
    return getattr(_thread_locals, 'user', None)

class ThreadLocals(object):
    """Middleware that gets various objects from the
    request object and saves them in thread local storage."""
    def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)

#### end

And in the Custom manager you could call the get_current_user() method to return only objects a specific user created.

class UserContactManager(models.Manager):
    def get_query_set(self):
        return super(UserContactManager, self).get_query_set().filter(creator=get_current_user())

Is this a good approach to this use-case? Will this work? Or is this like "using a sledgehammer to crack a nut" ? ;-)

Just using:

Contact.objects.filter(created_by= user)

in each view doesn`t look very neat to me.

EDIT Do not use this middleware approach !!!

use the approach stated by Jack M. below

After a while of testing this approach behaved pretty strange and with this approach you mix up a global-state with a current request.

Use the approach presented below. It is really easy and no need to hack around with the middleware.

create a custom manager in your model with a function that expects the current user or any other user as an input.

#in your models.py
class HourRecordManager(models.Manager):
    def for_user(self, user):
        return self.get_query_set().filter(created_by=user)

class HourRecord(models.Model):
    #Managers
    objects = HourRecordManager()

#in vour view you can call the manager like this and get returned only the objects from the currently logged-in user.

hr_set = HourRecord.objects.for_user(request.user)

See also this discussion about the middelware approach.


Solution

  • One way to handle this would be to create a new method instead of redefining get_query_set. Something along the lines of:

    class UserContactManager(models.Manager):
        def for_user(self, user):
            return super(UserContactManager, self).get_query_set().filter(creator=user)
    
    class UserContact(models.Model):
        [...]
        objects = UserContactManager()
    

    This allows your view to look like this:

    contacts = Contact.objects.for_user(request.user)
    

    This should help keep your view simple, and because you would be using Django's built in features, it isn't likely to break in the future.