Making User info available outside requests
A pretty common requirement is to have the current user's info available outside requests. Most of the time, the simplest, easiest and best way to handle this is to write a function, or a method on your model, which accepts the user as an argument; then your view function can simply call it and pass in the user. What follows below is a hackish solution which makes the user available in a far more complex way; it's almost always better to avoid the solution below in favor of writing better-designed code which correctly passes the user around as an argument, however.
Note This technique has become obsolete in the latest django.contrib.admin. If you were previously using this technique, it is strongly suggested that you stop and switch to using built-in functionality of the admin instead. See CookBookNewformsAdminAndUser for one approach or (best solution) take a look at django/contrib/admin/options.py to look at the methods you can override in there -- everything from form creation to object saving is completely customizable, and the methods all have access to the current HTTP request.
Let's suppose you have a model like the following:
from django.db import models from django.contrib.auth.models import User class NewsItem(models.Model): owner = models.ForeignKey(User,related_name="owner",blank=True, editable=False) last_edited_by = models.ForeignKey(User,related_name="news_edited_last",blank=True, editable=False) text = models.TextField()
You want to set the owner when a news item is added, and set last_edited_by when a news item is changed. What now? You can't get the user object outside of requests! Fortunately, there's a middleware that makes it possible.
Create a new "middleware" directory in your project directory. Create an empty __init__.py file inside the "middleware" directory. Now, create a "threadlocals.py" file in the "middleware" directory, with the following contents:
# threadlocals middleware try: from threading import local except ImportError: 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)
In your project's settings.py file, add "yourproject.middleware.threadlocals.ThreadLocals" to the MIDDLEWARE_CLASSES dir, e.g.:
MIDDLEWARE_CLASSES = ( "django.middleware.common.CommonMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "yourproject.middleware.threadlocals.ThreadLocals", )
Now you only need to override your model's save() method to set the owner and last_edited_by when appropriate:
from django.db import models from django.contrib.auth.models import User from yourproject.middleware import threadlocals class NewsItem(models.Model): owner = models.ForeignKey(User,related_name="owner",blank=True) last_edited_by = models.ForeignKey(User,related_name="last_edited_by",blank=True) text = models.TextField() # Set the owner and last_edited_by when appropriate def save(self): # If the object already existed, it will already have an id if self.id: # This object is being edited, not saved, set last_edited_by self.last_edited_by = threadlocals.get_current_user() else: # This is a new object, set the owner self.owner = self.last_edited_by = threadlocals.get_current_user() super(NewsItem,self).save()
Now, when you save a NewsItem object, the owner and last_edited_by fields are set automatically. That's it! Not that difficult, eh :) You can get the user object anywhere, all you need to to is to call the get_current_user() method.
Tip:
It is important not to specify the project in the path to your middleware class in MIDDLEWARE_CLASSES when using applications within your project and the threadlocals is defined within an application. If not, the middleware does seem to work, but the get_current_user() method will return 'None'. So use:
myapp.middleware.threadlocals.ThreadLocals
instead of:
myproject.myapp.middleware.threadlocals.ThreadLocals
(This has been tested on Django version 0.96.1 and 1.0 final)
