Skip to content

Commit 686d569

Browse files
committed
fix(drf): honor Django request filtering
1 parent b3e146b commit 686d569

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

‎posthog/integrations/drf.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,25 @@ def _configured_client(client: Optional[Client]) -> Optional[Client]:
6666
return None
6767

6868

69+
def _passes_django_request_filter(context: Mapping[str, Any]) -> bool:
70+
"""Apply the Django middleware request filter to the underlying request."""
71+
try:
72+
from django.conf import settings
73+
74+
request_filter = getattr(settings, "POSTHOG_MW_REQUEST_FILTER", None)
75+
except Exception:
76+
return True
77+
78+
request = context.get("request")
79+
if not callable(request_filter) or request is None:
80+
return True
81+
82+
# DRF wraps Django's HttpRequest. Pass the same object that
83+
# PosthogContextMiddleware evaluates so filters behave consistently.
84+
django_request = getattr(request, "_request", request)
85+
return bool(request_filter(django_request))
86+
87+
6988
def _capture_exception(client: Optional[Client], exc: Exception) -> None:
7089
if _exception_is_already_captured(exc):
7190
return
@@ -101,6 +120,10 @@ def create_exception_handler(
101120
exception_filter: Optional final filter called with ``(exception,
102121
response, context)``. Returning ``False`` suppresses capture.
103122
123+
When ``POSTHOG_MW_REQUEST_FILTER`` is configured, it is also applied to the
124+
underlying Django request before capture so handled errors cannot bypass the
125+
middleware's per-request exclusion.
126+
104127
The delegated handler is called first. A ``None`` response is never
105128
captured here because DRF will re-raise that exception, allowing Django's
106129
middleware to capture it as unhandled.
@@ -117,6 +140,8 @@ def posthog_exception_handler(exc: Exception, context: Mapping[str, Any]) -> Any
117140
should_capture = status_code >= 500 or (
118141
capture_4xx and 400 <= status_code < 500
119142
)
143+
if should_capture:
144+
should_capture = _passes_django_request_filter(context)
120145
if should_capture and exception_filter is not None:
121146
should_capture = bool(exception_filter(exc, response, context))
122147
if should_capture:

‎posthog/test/integrations/test_drf_integration.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import django
66
from django.conf import settings
7+
from django.test import override_settings
78

89
if not settings.configured:
910
settings.configure(
@@ -102,6 +103,35 @@ def test_custom_handler_exception_is_preserved(self):
102103

103104
client.capture_exception.assert_not_called()
104105

106+
@override_settings(POSTHOG_MW_REQUEST_FILTER=lambda request: False)
107+
def test_django_middleware_request_filter_suppresses_capture(self):
108+
client = Mock()
109+
handler = create_exception_handler(
110+
lambda exc, context: Response(status=500), client=client
111+
)
112+
django_request = object()
113+
drf_request = Mock(_request=django_request)
114+
115+
response = handler(RuntimeError("filtered"), {"request": drf_request})
116+
117+
self.assertEqual(response.status_code, 500)
118+
client.capture_exception.assert_not_called()
119+
120+
def test_django_middleware_request_filter_receives_underlying_request(self):
121+
client = Mock()
122+
request_filter = Mock(return_value=True)
123+
handler = create_exception_handler(
124+
lambda exc, context: Response(status=500), client=client
125+
)
126+
django_request = object()
127+
drf_request = Mock(_request=django_request)
128+
129+
with override_settings(POSTHOG_MW_REQUEST_FILTER=request_filter):
130+
handler(RuntimeError("tracked"), {"request": drf_request})
131+
132+
request_filter.assert_called_once_with(django_request)
133+
client.capture_exception.assert_called_once()
134+
105135
def test_exception_filter_can_suppress_capture(self):
106136
client = Mock()
107137
exception_filter = Mock(return_value=False)

0 commit comments

Comments
 (0)