Skip to content

Commit b6d2419

Browse files
Michael Gallerfelixxm
Michael Galler
authored andcommitted
[3.1.x] Fixed #31905 -- Made MiddlewareMixin call process_request()/process_response() with thread sensitive.
Co-authored-by: Carlton Gibson <[email protected]> Backport of 547a07f from master
1 parent e81aa7a commit b6d2419

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

django/utils/deprecation.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,16 @@ async def __acall__(self, request):
123123
"""
124124
response = None
125125
if hasattr(self, 'process_request'):
126-
response = await sync_to_async(self.process_request)(request)
126+
response = await sync_to_async(
127+
self.process_request,
128+
thread_sensitive=True,
129+
)(request)
127130
response = response or await self.get_response(request)
128131
if hasattr(self, 'process_response'):
129-
response = await sync_to_async(self.process_response)(request, response)
132+
response = await sync_to_async(
133+
self.process_response,
134+
thread_sensitive=True,
135+
)(request, response)
130136
return response
131137

132138
def _get_response_none_deprecation(self, get_response):

docs/releases/3.1.1.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ Bugfixes
3535
* Reverted a deprecation in Django 3.1 that caused a crash when passing
3636
deprecated keyword arguments to a queryset in
3737
``TemplateView.get_context_data()`` (:ticket:`31877`).
38+
39+
* Enforced thread sensitivity of the :class:`MiddlewareMixin.process_request()
40+
<django.utils.deprecation.MiddlewareMixin>` and ``process_response()`` hooks
41+
when in an async context (:ticket:`31905`).

tests/deprecation/test_middleware_mixin.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import threading
2+
3+
from asgiref.sync import async_to_sync
4+
15
from django.contrib.sessions.middleware import SessionMiddleware
6+
from django.db import connection
7+
from django.http.request import HttpRequest
8+
from django.http.response import HttpResponse
29
from django.middleware.cache import (
310
CacheMiddleware, FetchFromCacheMiddleware, UpdateCacheMiddleware,
411
)
512
from django.middleware.common import CommonMiddleware
613
from django.middleware.security import SecurityMiddleware
714
from django.test import SimpleTestCase
8-
from django.utils.deprecation import RemovedInDjango40Warning
15+
from django.utils.deprecation import MiddlewareMixin, RemovedInDjango40Warning
916

1017

1118
class MiddlewareMixinTests(SimpleTestCase):
@@ -37,3 +44,37 @@ def test_subclass_deprecation(self):
3744
with self.subTest(middleware=middleware):
3845
with self.assertRaisesMessage(RemovedInDjango40Warning, self.msg):
3946
middleware()
47+
48+
def test_sync_to_async_uses_base_thread_and_connection(self):
49+
"""
50+
The process_request() and process_response() hooks must be called with
51+
the sync_to_async thread_sensitive flag enabled, so that database
52+
operations use the correct thread and connection.
53+
"""
54+
def request_lifecycle():
55+
"""Fake request_started/request_finished."""
56+
return (threading.get_ident(), id(connection))
57+
58+
async def get_response(self):
59+
return HttpResponse()
60+
61+
class SimpleMiddleWare(MiddlewareMixin):
62+
def process_request(self, request):
63+
request.thread_and_connection = request_lifecycle()
64+
65+
def process_response(self, request, response):
66+
response.thread_and_connection = request_lifecycle()
67+
return response
68+
69+
threads_and_connections = []
70+
threads_and_connections.append(request_lifecycle())
71+
72+
request = HttpRequest()
73+
response = async_to_sync(SimpleMiddleWare(get_response))(request)
74+
threads_and_connections.append(request.thread_and_connection)
75+
threads_and_connections.append(response.thread_and_connection)
76+
77+
threads_and_connections.append(request_lifecycle())
78+
79+
self.assertEqual(len(threads_and_connections), 4)
80+
self.assertEqual(len(set(threads_and_connections)), 1)

0 commit comments

Comments
 (0)