Django: Get current user in model save

后端 未结 6 1585
既然无缘
既然无缘 2020-12-04 23:29

I want to get the currently logged in user (request.user) in the save method of models.py. I want to check the role of the user and see if it can p

相关标签:
6条回答
  • 2020-12-05 00:00

    Have you tried using the following library:

    https://pypi.org/project/django-currentuser/

    Snippet of how it is used from the website:

    from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
    # As model field:
    from django_currentuser.db.models import CurrentUserField
    class Foo(models.Model):
        created_by = CurrentUserField()
    
    0 讨论(0)
  • 2020-12-05 00:02

    The correct way is to use threading.local, instead of using a dictionary with threading.current_thread, since it will lead to a memory leak, since the old values will stay there for as long the application is running:

    import threading
    
    request_local = threading.local()
    
    def get_request():
        return getattr(request_local, 'request', None)
    
    class RequestMiddleware():
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            request_local.request = request
            return self.get_response(request)
    
        def process_exception(self, request, exception):
            request_local.request = None
    
        def process_template_response(self, request, response):
            request_local.request = None
            return response
    

    If you want to access the user instead of the request, you can do get_request().user, or save the user instead of the request on __call__

    0 讨论(0)
  • 2020-12-05 00:03

    You can tackle this problem from another angle. Instead of changing the models save method you should override the AdminSites save_model method. There you'll have the request object and can access the logged in user data as you already pointed out.

    Have a look at this chapter of the docs: Django ModelAdmin documentation save_model

    0 讨论(0)
  • 2020-12-05 00:04

    I found a way to do that, it involves declaring a MiddleWare, though. Create a file called get_username.py inside your app, with this content:

    from threading import current_thread
    
    _requests = {}
    
    def get_username():
        t = current_thread()
        if t not in _requests:
             return None
        return _requests[t]
    
    class RequestMiddleware(object):
        def process_request(self, request):
            _requests[current_thread()] = request
    

    Edit your settings.py and add it to the MIDDLEWARE_CLASSES:

    MIDDLEWARE_CLASSES = (
        ...
        'yourapp.get_username.RequestMiddleware',
    )
    

    Now, in your save() method, you can get the current username like this:

    from get_username import get_username
    
    ...
    
    def save(self, *args, **kwargs):
        req = get_username()
        print "Your username is: %s" % (req.user)
    
    0 讨论(0)
  • 2020-12-05 00:05

    I don't think that save_model method override is the best option. Imagine, for instance, that you want to save the user info or validate the model based on user info and that save() does not come from a view or the adminsite itself.

    What people are asking are constructions like those one:

    def save(..)
        self.user = current_user()
    

    or

    def save(..)
        user = current_user()
        if user.group == 'XPTO':
            error('This user cannot edit this record')
    

    The best approach I found so far is:

    https://bitbucket.org/q/django-current-user/overview

    0 讨论(0)
  • 2020-12-05 00:06

    The solution proposed by @nKn is good starting point, but when I tried to implemented today, I faced two issues:

    1. In the current Django version middleware created as plain object doesn't work.
    2. Unittests are failing (since they usually run in single thread, so your 'request' will can be sticked between two consequent tests if the first test has HTTP-request and the second hasn't).

    Here is my updated middleware code, which works with Django 1.10 and doesn't break unittests:

    from threading import current_thread
    
    from django.utils.deprecation import MiddlewareMixin
    
    
    _requests = {}
    
    
    def current_request():
        return _requests.get(current_thread().ident, None)
    
    
    class RequestMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
            _requests[current_thread().ident] = request
    
        def process_response(self, request, response):
            # when response is ready, request should be flushed
            _requests.pop(current_thread().ident, None)
            return response
    
        def process_exception(self, request, exception):
            # if an exception has happened, request should be flushed too
             _requests.pop(current_thread().ident, None)
    
    0 讨论(0)
提交回复
热议问题