Skip to content

Commit 2293daa

Browse files
authored
Merge pull request #113 from ghazi-git/document-404-due-to-format-query-parameter
Document 404 error response when the API accepts a format query parameter or a url suffix for content negotiation
2 parents 7ce210f + 1d41e4c commit 2293daa

File tree

5 files changed

+41
-2
lines changed

5 files changed

+41
-2
lines changed

docs/changelog.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## [UNRELEASED]
88
### Added
9-
- add support for python 3.14
9+
- Add support for python 3.14
1010

1111
### Changed (backward-incompatible)
12-
- set minimum version of drf-spectacular to 0.29.0
12+
- Set the minimum version of drf-spectacular to 0.29.0
13+
- Account for the `format` query parameter raising 404 errors when generating the API schema for error responses. This
14+
will result in any project using the default value for `REST_FRAMEWORK["URL_FORMAT_OVERRIDE"]"` showing 404 errors for
15+
every operation. That's because DRF enables the `format` query parameter in every endpoint by default. If you don't
16+
need the `format` query parameter for content negotiation, you can disable it with by setting `"URL_FORMAT_OVERRIDE"`
17+
to `None` in DRF settings. Refer
18+
to [DRF docs](https://www.django-rest-framework.org/api-guide/settings/#url_format_override) for more information.
1319

1420
## [0.15.0] - 2025-06-09
1521
### Added

docs/openapi.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ class CustomSerializer(serializers.Serializer):
107107

108108
## Tips and Tricks
109109

110+
### Hide 404 error responses due to the `format` query parameter
111+
112+
Set `REST_FRAMEWORK["URL_FORMAT_OVERRIDE"]"` to `None` if you're not relying on a query parameter for content
113+
negotiation. This avoids showing a 404 error response in the API schema for every operation.
114+
115+
By default, DRF sets the value for `REST_FRAMEWORK["URL_FORMAT_OVERRIDE"]"` to `format`. That allows API consumers to
116+
send a `format` query parameter in every operation for content negotiation. If the API cannot handle the requested
117+
`format`, it will return a 404 error response. From the perspective of this package, that means every operation can
118+
return a 404 response when using the default content negotiator. Refer
119+
to [DRF docs](https://www.django-rest-framework.org/api-guide/settings/#url_format_override) for more information.
120+
110121
### Hide error responses that show in every operation
111122

112123
By default, the error response for all supported status codes will be added to the schema. Some of these status

drf_standardized_errors/openapi.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from rest_framework.pagination import CursorPagination, PageNumberPagination
1818
from rest_framework.parsers import FileUploadParser, JSONParser, MultiPartParser
1919
from rest_framework.permissions import AllowAny, IsAuthenticated
20+
from rest_framework.settings import api_settings as drf_settings
2021
from rest_framework.versioning import (
2122
AcceptHeaderVersioning,
2223
HostNameVersioning,
@@ -203,10 +204,17 @@ def _should_add_http404_error_response(self) -> bool:
203204
if parameter["in"] == "path"
204205
]
205206
)
207+
# the default content negotiator can raise a 404 when no renderer can handle
208+
# to the format parameter in the URL
209+
content_negotiator = self.view.get_content_negotiator()
210+
view_can_have_no_renderers = (
211+
self.view.format_kwarg or drf_settings.URL_FORMAT_OVERRIDE
212+
) and isinstance(content_negotiator, DefaultContentNegotiation)
206213
return (
207214
paginator_can_raise_404
208215
or versioning_scheme_can_raise_404
209216
or has_path_parameters
217+
or view_can_have_no_renderers
210218
)
211219

212220
def _should_add_http405_error_response(self) -> bool:

tests/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"DEFAULT_AUTHENTICATION_CLASSES": [],
2424
"DEFAULT_PERMISSION_CLASSES": [],
2525
"TEST_REQUEST_DEFAULT_FORMAT": "json",
26+
"URL_FORMAT_OVERRIDE": None,
2627
"DEFAULT_SCHEMA_CLASS": "drf_standardized_errors.openapi.AutoSchema",
2728
}
2829

tests/test_openapi.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,19 @@ def test_404_error_when_url_parameters():
381381
assert "404" in responses
382382

383383

384+
def test_404_error_when_url_format_enabled(settings):
385+
settings.REST_FRAMEWORK = {
386+
**settings.REST_FRAMEWORK,
387+
"URL_FORMAT_OVERRIDE": "format",
388+
}
389+
390+
route = "not-found/"
391+
view = DummyView.as_view()
392+
schema = generate_view_schema(route, view)
393+
responses = get_responses(schema, route)
394+
assert "404" in responses
395+
396+
384397
def test_no_404_error():
385398
route = "not-found/"
386399
view = DummyView.as_view()

0 commit comments

Comments
 (0)