From 842f699e8aac4a40435ea8f130c5beb7c593a9e2 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 18 Jan 2023 11:57:15 +0100 Subject: [PATCH] django32: change the way Thread are made tenant aware (#67760) Django 3.2. changed the implementation of django.db.ConnectionHandler it now uses asgiref.local.Local as a thread/asyncio-task local dictionnary instead of threading.local. This new implementation use threading.current_thread() to get a reference to the current thread instead of threading._get_ident(), but inside __bootstrap_inner, the _active dictionnary is not initialized and current_thread() returns a dummy value instead of the current thread. To work around this behaviour I made __bootstrap_inner wrap the run method with the code needed to setup the tenant, so that it's run after __boostrap_inner initialization of the current thread in the _active dictionnary. --- hobo/multitenant/threads.py | 42 ++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/hobo/multitenant/threads.py b/hobo/multitenant/threads.py index 46f3f9f..bc7387e 100644 --- a/hobo/multitenant/threads.py +++ b/hobo/multitenant/threads.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import functools import threading _Thread_start = threading.Thread.start @@ -28,21 +29,34 @@ def _new_start(self): return _Thread_start(self) +def wrap_run(self, func): + if getattr(func, '_wrapped', False): + return func + + @functools.wraps(func) + def wrapper(): + tenant = getattr(self, 'tenant', None) + + if tenant is not None: + from django.db import connection + + old_tenant = connection.tenant + connection.set_tenant(self.tenant) + try: + func() + finally: + connection.set_tenant(old_tenant) + connection.close() + else: + func() + + wrapper._wrapped = True + return wrapper + + def _new__bootstrap_inner(self): - tenant = getattr(self, 'tenant', None) - - if tenant is not None: - from django.db import connection - - old_tenant = connection.tenant - connection.set_tenant(self.tenant) - try: - _Thread__bootstrap_inner(self) - finally: - connection.set_tenant(old_tenant) - connection.close() - else: - _Thread__bootstrap_inner(self) + self.run = wrap_run(self, self.run) + _Thread__bootstrap_inner(self) def install_tenant_aware_threads():