Django: Get current user in model save
The solution proposed by @nKn is good starting point, but when I tried to implemented today, I faced two issues:
- In the current Django version middleware created as plain object doesn't work.
- 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)
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__
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)
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