What's Changed
Breaking changes
- Replaced
Sentinelclass in all typing annotations to beEMPTY
sentinel value, since all type-checkers support this now, #1687 - Replaced multiple types from
Anyto beTypeForm[Any], #1688 - Changed parameters and return type of
csrf_security_scheme, #1718 - Changed CSRF security scheme name with
CSRF_USE_SESSIONSenabled, #1718
Performance
- Optimized
SerializerContext.__call__for common cases: zero components,
and a singleBodyMsgspeccomponent, now users can also specify their own
parsing strategies for other contexts. Context parsing is around x1.3 faster
for endpoints without components and around x1.6 faster for a single
BodyMsgspeccomponent, #1740
Features
- Python 3.15 official support, #1675 and #1762
- Added
MsgspecSerializer.enable_component_gcflag to optionally
enable the GC for the component parsing model, #1697 - Added
HeaderSpec.typeto document non-string response headers.
ThrottlingRetry-AfterandX-RateLimit-*headers are now
documented as integers with examples instead of strings, #1706 - Added
Parser.decodes_into_modelflag for parsers
that decode bodies directly into models, #1740 - Added reusable
BaseJWTokenHandlerfor JWT processing
independent of user resolution, #1779
Bugfixes
- Fixed
build_404_handler,build_500_handler, andbuild_csrf_handler
not matching their prefixes when the path starts with a language
ofi18n_patterns, like/en/api/missing/. Such requests got
the default Django HTML error page instead of the API error.
Added thelanguagesargument to choose which languages are allowed,
by default all languages of Django are allowed, #1702 - Fixed streaming with
Streaming(ping_seconds=...)leaving the pending
event task running when the stream is cancelled or closed, for example,
on a client disconnect. Closing the events producer failed with
RuntimeError: aclose(): asynchronous generator is already running
and the task later failed on the already closed response, #1791 - Fixed login
concrete_viewsinheritingsecurity
fromOpenAPIConfig, they now setsecurity = None
and are documented as public endpoints, #1719 - Fixed
jwt_expirationandjwt_refresh_expirationpassed toas_view
of the cookieconcrete_viewschanging the tokens, but not their cookies.
They are typedas_viewarguments now and are set on the class, #1690 - Fixed
BodyMsgspecbeing allowed with parsers
that don't decode into models, likeJsonParseror forms.
It raisesEndpointMetadataErrornow, #1740 - Fixed Django's decorators that support async views, like
condition
orcache_control, failing on async controllers withwrap_middleware.
They got a sync view that returned a coroutine, so they treated
the coroutine as a response, #1684 - Fixed endpoints returning
Nonedocumenting a responsecontent
of{"type": "null"}while sending an empty body, which made
generated clients parse a body that doesn't exist, #1733 - Fixed a
$refoutside of#/components/schemas/in aQuery,Headers,
PathorCookiesmodel raisingKeyErrorwhen building the schema, #1708 - Fixed
SyncRedisandAsyncRediswithSimpleRatereporting
a negative reset (X-RateLimit-Reset: -2) for a throttle with
no requests in the current window, other backends report the full window - Fixed throttles with the same cache key name getting the same policy name
inRateLimit-PolicyandRateLimitheaders ofRateLimitIETFDraft.
Now we add the window to the name, likeRemoteAddr-60,
and the number of requests too, when the window is the same, #1705 - Fixed
RateLimit-Policyheader ofRateLimitIETFDraftnot following
the IETF draft. Now every policy is a quoted name withqandw
parameters, like"RemoteAddr";q=5;w=60, #1703 - Responses that
wrap_middlewarereturns without calling the controller
are now validated after the converter, like the responses of endpoints,
as the middleware docs promised. A converter that returns something
itsResponseSpecdoes not describe, or a status code
that is not documented, now fails with422
wherevalidate_responsesis on, #1682 - Fixed
FileMetadataComponent.validate()always usingany()to check
if parsers support file parsing. WhenFileMetadatais required, all parsers
must now supportSupportsFileParsing. When it is optional, at least one
must, #1493 - Fixed that CSRF auth schemes were not showing
csrf_headerscheme, #1718
Misc
- Suggest
concrete_viewsinstead ofviewsby default indmrskill, #1670 - Mention typed expirations of cookie
concrete_viewsindmrskill, #1690 - Updated documentation and agent skills to link to
llms.txtinstead of
the removedllms-full.txt, #1727 - Fixed the
login_requiredexample in the middleware docs:
its redirect was never converted to401,
because302was not in the listed responses, #1682 dmrskill now turns CSRF checks on withcsrf_exempt = False
instead of wrappingcsrf_protect, and itswrap_middlewareexample
documents the304of Django'sconditiondecorator, #1671- Added isiXhosa (
xh) translation, #718
New Contributors
- @joefefs made their first contribution in #1734
- @mayuriphad made their first contribution in #1748
- @lalmachado made their first contribution in #1721
- @nkhitrov made their first contribution in #1757
- @PhumelelaM7 made their first contribution in #1769
- @chaitanyakelkar27 made their first contribution in #1770
- @noregrets718 made their first contribution in #1773
- @2025sl93041-gkb made their first contribution in #1753
- @ganesan33 made their first contribution in #1718
- @gptfei made their first contribution in #1756
- @Nordm4n made their first contribution in #1779
Full Changelog: 0.16.0...0.17.0