Skip to content

Commit f4449b5

Browse files
fix: defer Django middleware injection when settings are not yet configured
1 parent 1751303 commit f4449b5

1 file changed

Lines changed: 44 additions & 5 deletions

File tree

drift/instrumentation/django/instrumentation.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,42 @@ def _resolve_http_transforms(
4949
@override
5050
def patch(self, module: ModuleType) -> None:
5151
"""Patch Django by injecting middleware."""
52+
if not self._try_inject_middleware():
53+
# Settings not configured yet — defer injection until django.setup() runs
54+
self._defer_middleware_injection()
55+
56+
def _try_inject_middleware(self) -> bool:
57+
"""Attempt to inject DriftMiddleware into Django settings.
58+
59+
Returns:
60+
True if middleware was injected (or already present), False if
61+
settings are not yet configured and injection should be deferred.
62+
"""
5263
global _middleware_injected
5364

5465
if _middleware_injected:
5566
logger.debug("Middleware already injected, skipping")
56-
return
67+
return True
5768

5869
try:
5970
from django.conf import settings
6071

6172
if not settings.configured:
62-
logger.warning("Django settings not configured, cannot inject middleware")
63-
return
73+
logger.debug("Django settings not configured yet, will defer middleware injection")
74+
return False
6475

6576
middleware_setting = self._get_middleware_setting(settings)
6677
if not middleware_setting:
6778
logger.warning("Could not find middleware setting, cannot inject")
68-
return
79+
return True # Don't retry — this won't change
6980

7081
current_middleware = list(getattr(settings, middleware_setting, []))
7182

7283
middleware_path = "drift.instrumentation.django.middleware.DriftMiddleware"
7384
if middleware_path in current_middleware:
7485
logger.debug("DriftMiddleware already in settings, skipping injection")
7586
_middleware_injected = True
76-
return
87+
return True
7788

7889
# Insert at position 0 to capture all requests
7990
current_middleware.insert(0, middleware_path)
@@ -89,11 +100,39 @@ def patch(self, module: ModuleType) -> None:
89100
self._force_database_reconnect()
90101

91102
print("Django instrumentation applied")
103+
return True
92104

93105
except ImportError as e:
94106
logger.warning(f"Could not import Django settings: {e}")
107+
return True # Don't retry on import errors
95108
except Exception as e:
96109
logger.error(f"Failed to inject middleware: {e}", exc_info=True)
110+
return True # Don't retry on unexpected errors
111+
112+
def _defer_middleware_injection(self) -> None:
113+
"""Monkey-patch django.setup() to inject middleware after settings are configured.
114+
115+
When TuskDrift.initialize() runs before DJANGO_SETTINGS_MODULE is set
116+
(common in manage.py where the SDK init is the first import), Django
117+
settings aren't available yet. This defers injection to run after
118+
django.setup() completes, which is when settings are guaranteed to be
119+
configured.
120+
"""
121+
import django
122+
123+
original_setup = django.setup
124+
125+
def patched_setup(*args, **kwargs):
126+
# Restore original setup first to avoid re-entrance
127+
django.setup = original_setup
128+
# Run the original django.setup()
129+
result = original_setup(*args, **kwargs)
130+
# Now settings are configured — inject middleware
131+
self._try_inject_middleware()
132+
return result
133+
134+
django.setup = patched_setup
135+
logger.debug("Deferred middleware injection to django.setup()")
97136

98137
def _force_database_reconnect(self) -> None:
99138
"""Force Django to close and recreate database connections."""

0 commit comments

Comments
 (0)