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

15,920

Solution 1

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.

Solution 2

It seems necessary to use the middleware to store the user information.

However, I'd rather not modify the default ModelManager objects, but hook it upto a different manager, that I will use in the code, say in your case user_objects instead of objects.

Since you will use this only within views that are @login_required you dont need all the complex error handling in the Middleware.

Just my 2¢.

Share:
15,920
Thomas Kremmel
Author by

Thomas Kremmel

Updated on June 03, 2022

Comments

  • Thomas Kremmel
    Thomas Kremmel about 2 years

    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.