Why is using thread locals in Django bad?

I disagree entirely. TLS is extremely useful. It should be used with care, just as globals should be used with care; but saying it shouldn't be used at all is just as ridiculous as saying globals should never be used.

For example, I store the currently active request in TLS. This makes it accessible from my logging class, without having to pass the request around through every single interface--including many that don't care about Django at all. It lets me make log entries from anywhere in the code; the logger outputs to a database table, and if a request happens to be active when a log is made, it logs things like the active user and what was being requested.

If you don't want one thread to have the capability of modifying another thread's TLS data, then set your TLS up to prohibit this, which probably requires using a native TLS class. I don't find that argument convincing, though; if an attacker can execute arbitrary Python code as your backend, your system is already fatally compromised--he could monkey patch anything to be run later as a different user, for example.

Obviously, you'll want to clear any TLS at the end of a request; in Django, that means clearing it in process_response and process_exception in a middleware class.


This question is really old, but I just saw someone referring to it, so I just want to note that the wiki page cited by this question stopped recommending threadlocal storage in 2010 and then was deleted altogether by 2012.


Despite the fact that you could mix up data from different users, thread locals should be avoided because they hide a dependency. If you pass arguments to a method you see and know what you're passing. But a thread local is something like a hidden channel in the background and you may wonder, that a method doesn't work correctly in some cases.

There are some cases where thread locals are a good choice, but they should be used rarely and carefully!


A quick example on how to create a TLS middleware compatible with the latest Django 1.10:

# coding: utf-8
# Copyright (c) Alexandre Syenchuk (alexpirine), 2016

try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()

def get_current_request():
    return getattr(_thread_locals, 'request', None)

def get_current_user():
    request = get_current_request()
    if request:
        return getattr(request, 'user', None)

class ThreadLocalMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        _thread_locals.request = request
        return self.get_response(request)