List of changes¶
Version history¶
We will follow Semantic Versions since 1.0.0 release.
While in Development Status :: 3 - Alpha - we will break
all the things without any notices.
After Development Status :: 4 - Beta we will still break things
but with a deprecation period.
What is a public API for us (all criteria must be met)?
Things that have public names
Things that live in public modules
Things that don’t live in
internal/orcompiled/Things that are explicitly documented in the docs
Later on we will make the API more stable and decrease the amount of requirements for an API to count as public.
Version 0.8.0 (2026-04-26)¶
Breaking changes¶
Breaking: Renamed
APIRedirectErrortoRedirectTo, #922Breaking: Split
BaseThrottleBackendintoBaseThrottleAsyncBackendandBaseThrottleSyncBackend, #942Breaking: Renamed
DjangoCacheintoSyncDjangoCache, addedAsyncDjangoCache, #942Breaking: Changed
BaseThrottleBackendAPI: now it requires.incrand.getmethods, the first one should ideally be an atomic increment, the second one is for reading objects only, #942Breaking: Removed
BaseThrottleAlgorithm.recordmethod, nowBaseThrottleAlgorithm.accessmust also record accesses. This will help to make throttling more atomic, #942
Migrations prompt¶
User-facing changes:
Apply this change to the code that uses `django-modern-rest`:
1. Replace `dmr.response.APIRedirectError` with `dmr.response.RedirectTo`
2. Replace `dmr.throttling.backend.DjangoCache`
with `dmr.throttling.backend.SyncDjangoCache` for sync throttles
and with `dmr.throttling.backend.AsyncDjangoCache` for async throttles
Features¶
Added
SyncRedisandAsyncRedisthrottling backends, #977Added
RefreshTokenSyncControllerandRefreshTokenAsyncControllerto issue new access/refresh token pairs from a valid refresh token, #907Added
validate_negotiationmetadata flag, so we can explicitly validate, that returned response followed the negotiation process, #711Added
accepted_headeras a faster alternative todjango’sHttpRequest.accepts, #854
Bugfixes¶
Fixed OpenAPI schema for Django session auth when
CSRF_USE_SESSIONS=True, #674Fixed that
itemSchemawas possible to be rendered in OpenAPI3.0.0and3.1.0, #908Fixed response validation when global error handler returns
HttpResponsewith a different content type than the negotiated renderer, #711Fixed
collectstaticfailure when usingManifestStaticFilesStorage, #927Fixed
datetimevalidation when using.to_response, #938Fixed a bug that
ObtainTokensAsyncControllerwas not setting therequest.auserattribute, #953Fixed a bug that
JWTSyncAuthwas not settingrequest.auser, #953Fixed
ResponseNegotiatorraisingNotAcceptableErroron streaming endpoints whenAccept: text/event-streamwas sent withoutapplication/json(the default browserEventSourcecase), which made 4xx/5xx error bodies and response validation crash with a 500 instead of rendering the configured non-streaming default, #962Fixed that original traceback was not shown for
BaseSchemaGenerator.get_schema, #961
Misc¶
Optimized
dmr_clientanddmr_rftest fixtures to usemsgspecfor JSON encoding and decoding when available, #889 and #976Optimized how per-endpoint throttle locks are used, #942
Version 0.7.0 (2026-04-14)¶
Breaking changes¶
Removed public
OpenAPIView.dumpscustomization hook, #847 If you customized schema output forOpenAPIJsonView, subclass the concrete view and override.get()instead. For JSON output, usedmr.openapi.core.dump.json_dumpif you need the framework’s default serializerBreaking:
get_jwtis renamed torequest_jwt, #868Breaking:
ResponseSpecProvider.provide_response_specsis now an instance method, #877Breaking: new required
routerparameter added toEndpoint.get_schemaandController.get_path_item, #879
Migration Prompt¶
Apply this change to the code that uses `django-modern-rest`:
1. Replace `OpenAPIView.dumps` usage with `dmr.openapi.core.dump.json_dump`
usage
2. Change `dmr.security.jwt.auth.get_jwt` function
to use `dmr.security.jwt.auth.request_jwt` instead, if user expects
to always get a token back, add `strict=True` argument
3. Change `provide_response_specs` class method to be instance method,
replace all `cls` usage with `self`
4. Add `router: Router` parameter to `Endpoint.get_schema`
and `Controller.get_path_item` methods
Features¶
Added official PyPy 3.11+ support, #870
Added
dmr.throttlingpackage, #877Added
request.__drm_auth__on all successful auth workflows, #868Added
request_authhelper function, #868Added
AuthenticatedHttpRequesttype for betterrequest: AuthenticatedHttpRequest[User]type annotations in controllers, #888Added
strictparameter torequest_rendererandrequest_parser, added@overloads to both of these functions, #869Added
ResponseSpecMetadatatype to represent headers and cookies with annotations, useful for error models, #882Allow individual
OpenAPIviews to skip schema validation, #867Added endpoint validator to prevent sync and async generator HTTP endpoints, #843
Added CSP-friendly templates for shipped
OpenAPIUI views, #847SwaggerView,RedocView,ScalarView, andStoplightViewnow avoid inline scripts in DMR-managed templates. Final CSP compatibility still depends on the upstream renderer bundle.Added
tagsanddeprecatedparameters toRouterfor OpenAPI metadata, #872. All operations in a router can now be grouped and marked as deprecated.
Bugfixes¶
Fixed that
OpenAPIwas revalidated on every.convertcall, #867Fixed missing
request.auser()afterJWTAsyncAuth, #884Fixed
ParameterMetadatamissing__slots__, #890Fixed
SSEventmissing__slots__, #901Fixed
SSEprotocol typing, #894Fixed a bug when we were treating controllers with no
api_endpointsas non-abstract, #894Fixed a bug when you were not able to subclass a controller with a serializer, #873
Misc¶
Added
dmrskill for agents to write betterdjango-modern-restcode, #886Switched from
Maketojustas a command runner
Version 0.6.0 (2026-04-09)¶
In this release we significantly increased the performance of pydantic
workflows by introducing PydanticFastSerializer.
No breaking changes in this release.
Features¶
Added
PydanticFastSerializerto serialize and deserializejsonobjects directly, #830Added support for complex
pydanticfields insideTypedDict,@dataclass, etc models, when usingPydanticSerializerandmsgspecparsers / renderers, #842Introduced official
to_json_kwargsandto_model_kwargsclass-level API formsgspecandpydanticserializers, #842Added “Problem Details” or RFC-9457 support, #78
Added customizable
json_moduleparameter toJsonParserandJsonRendererto support alternative JSON backends likeorjson, #857
Bugfixes¶
Fixed package metadata, #824
Fixed missing
style,phone,colorformats fromOpenAPIFormat, #842Fixes Django 5.2.13+ compat in
DMRAsyncRequestFactory, #853
Misc¶
Improved “Plugins” section in the docs, #835
Bumped
msgspecto0.21.0, #856Added official
SECURITY.mdpolicy
Version 0.5.0 (2026-04-05)¶
AKA “The first compiled version”.
This release will focus on better errors, performance, and stability.
No breaking changes in this release.
Features¶
Added
mypycsupport for compiling parts of the framework to run significantly faster, for example our compiled content negotiation is now 35 times faster then the Django’s default one, #202 See our https://django-modern-rest.readthedocs.io/en/latest/pages/deep-dive/performance.html#mypyc-compilation docs about thatAdded older Django versions
4.2,5.0,5.1official support, #803Added official
NamedTuplesupport, #774Added
timezoneandpydantic-extra-typesdependencies with[pydantic]extra, #802Added
exclude_semantic_responsesoptions, #786Added an option to override
exclude_semantic_responsesandno_validate_http_specsettings withNoneAdded a new way to resolve annotations for controllers:
AnnotationsContext, #787Added
yamlview for OpenAPI schema, #745
Bugfixes¶
Fixed
StreamingValidatorswallowing errors whenvalidate_eventswasTrue, but no event model was resolved, #780Fixed
dataclassinstances serialization withPydanticSerializerwithoutmsgspecjson renderer, #795Fixed missing
passwordOpenAPI format, #805Fixes incorrect settings validation, #821
Misc¶
Added
QuerySettutorial, #792Migrated from
poetrytouvfor dependency managementSet up automated secure publishing to PyPI, #823
Added CodSpeed integration for continuous performance monitoring, #810
Version 0.4.0 (2026-03-29)¶
AKA “The first version that I enjoy”.
Breaking changes¶
We changed how components are defined in controllers, #738 Now components will be defined in method parameters, not in base classes.
We removed
dmr.controller.Blueprint, because it is not needed anymore. It was used to compose different classes with different parsing strategies. Since, it was only used for different parsing rulesWe removed
dmr.routing.compose_blueprintsfunction, because there noBlueprints anymore :)We completely changed our SSE and streaming API, see #736 Old API was removed, new one was introduced.
dmr.ssepackage was moved todmr.streaming.sse
We always ship AI prompts to all breaking changes. So, it would be easier for you to migrate to a newer version using AI tool of your choice.
Migration Prompt¶
To migrate django-modern-rest to version 0.4.0 and above, you need to:
Load the latest documentation from https://django-modern-rest.readthedocs.io/llms-full.txt
Convert component parsing from old class-based API to new method-based API. Before:
from dmr import Blueprint, Body
from dmr.routing import compose_blueprints
from dmr.plugins.pydantic import PydanticSerializer
class UserCreateBlueprint(
Body[_UserInput], # <- needs a request body
Blueprint[PydanticSerializer],
):
def post(self) -> _UserOutput:
return _UserOutput(
uid=uuid.uuid4(),
email=self.parsed_body.email,
age=self.parsed_body.age,
)
class UserListBlueprint(Blueprint[PydanticSerializer]):
def get(self) -> list[_UserInput]:
return [
_UserInput(email='first@example.org', age=1),
_UserInput(email='second@example.org', age=2),
]
UsersController = compose_blueprints(UserCreateBlueprint, UserListBlueprint)
To:
from dmr import Controller, Body
from dmr.plugins.pydantic import PydanticSerializer
class UsersController(Controller[PydanticSerializer]):
def get(self) -> list[_UserInput]:
return [
_UserInput(email='first@example.org', age=1),
_UserInput(email='second@example.org', age=2),
]
def post(self, parsed_body: Body[_UserInput]) -> _UserOutput:
return _UserOutput(
uid=uuid.uuid4(),
email=self.parsed_body.email,
age=self.parsed_body.age,
)
Replace all
Blueprintandcompose_blueprintsreferences with a new API: Instead you must useControllerand different methods under a single classNow, change all
@sse-based controllers to newSSEControllerAPI, from:
from collections.abc import AsyncIterator
import msgspec
from django.http import HttpRequest
from dmr.components import Headers
from dmr.plugins.msgspec import MsgspecSerializer
from dmr.sse import SSEContext, SSEResponse, SSEvent, sse
class HeaderModel(msgspec.Struct):
last_event_id: int | None = msgspec.field(
default=None,
name='Last-Event-ID',
)
async def produce_user_events(
request_headers: HeaderModel,
) -> AsyncIterator[SSEvent[str]]:
if request_headers.last_event_id:
yield SSEvent(f'starting from {request_headers.last_event_id}')
else:
yield SSEvent('starting from scratch')
@sse(MsgspecSerializer, headers=Headers[HeaderModel])
async def user_events(
request: HttpRequest,
context: SSEContext[None, None, HeaderModel],
) -> SSEResponse[SSEvent[str]]:
return SSEResponse(produce_user_events(context.parsed_headers))
To:
from collections.abc import AsyncIterator
import msgspec
from dmr.components import Headers
from dmr.plugins.msgspec import MsgspecSerializer
from dmr.streaming.sse import SSEController, SSEvent
class HeaderModel(msgspec.Struct):
last_event_id: int | None = msgspec.field(
default=None,
name='Last-Event-ID',
)
class UserEventsController(SSEController[MsgspecSerializer]):
def get(
self,
parsed_headers: Headers[HeaderModel],
) -> AsyncIterator[SSEvent[str]]:
return self.produce_user_events(parsed_headers)
async def produce_user_events(
self,
parsed_headers: HeaderModel,
) -> AsyncIterator[SSEvent[str]]:
if parsed_headers.last_event_id is None:
yield SSEvent('starting from scratch')
else:
yield SSEvent(f'starting from {parsed_headers.last_event_id}')
Replace old
dmr.sseimports with newdmr.streaming.ssealternatives
Features¶
Added
@attrs.defineofficial support, #706Added
msgpackparser and renderer, #630Added
JsonLinesorJsonLsupport, #607Added
pingevents toSSEstreaming, #606Added
SSEsupport for non-GETmethods,Bodycomponent parsing, #736Added
i18nsupport for user-facing error messages using Django’sgettext_lazy, #426Added
MediaTypevalidation for the defaultencodingfield and OpenAPI 3.2itemEncodingandprefixEncodingfields, #695Added
MediaTypeMetadatametadata item to set required parameters for theMediaTyperequest body forBodyandFileMetadatacomponents, #695 and #698Added support for Swagger, Redoc, and Scalar CDN configuration, #678
Added TraceCov integration for API coverage tracking in test suites, including automatic request tracking for
dmr_clientanddmr_async_client, #735.Added Stoplight Elements UI for OpenAPI documentation, #748
Added better
settingsfixture support forpytestplugin, #769
Bugfixes¶
Fixed
SSEcontrollers__name__and__doc__generation via@ssedecorator, #700Fixed a bug where
FileMetadatarendered list of schemas incorrectly, #698
Misc¶
Added
$dmr-openapi-skeletonAI agent skill, #693Added
$dmr-from-django-ninjaAI agent skill, #693Added
$dmr-from-drfAI agent skill, #744Added ETag usage docs, #699
Added multiple translations for the user-facing error messages, #718
Now
MsgspecJsonRendererandJsonRendererproduce the samejsonstring in terms of whitespaces, #736
Version 0.3.0 (2026-03-17)¶
Features¶
Added
FileResponseSpecand improvedFileResponseschema generation, #682Added
encoding:support for file media types inFileMetadata, #682
Bugfixes¶
Fixed OpenAPI schema for custom HTTP Basic auth headers, #672
Fixed JWT claim validation and error handling in
JWToken.decode, #675Fixed incorrect OpenAPI schema for
FileResponse, #682Fixed that
404was not listed in the endpoint’s metadata, when usingURLRoutewithoutPathcomponent, #685Fixed that
404was not documented in the OpenAPI whenPathcomponent was not used, butURLPatternhad parameters, #685Fixed
ValueErroron operation id generation, #685
Misc¶
Improved “Returning responses” docs, #684
Version 0.2.0 (2026-03-15)¶
Features¶
Breaking: Renamed
schema_onlyparameter toskip_validationAdded
dmr.routing.build_500_handlerhandler, #661Added support for
__dmr_split_commas__inHeaderscomponent, #659Added support for native Django urls to be rendered in the OpenAPI, now OpenAPI parameters will be generated even without
Pathcomponent, #659Do not allow
'\x00',\n, and\rasSSEvent.idandSSEvent.event, #667
Bugfixes¶
Fixes how
SSEResponseSpec.headers['Connection']header is specified, #654Fixed an
operation_idgeneration bug, #652Fixed a bug with parameter schemas were registered with no uses in the OpenAPI
Fixed a bug, when request to a missing page with wrong
Acceptheader was raising an error. Now it returns 406 as it should, #656Fixed fake examples generation, #638
Fixed OpenAPI schema for custom JWT auth parameters, #660
Fixed
Bodycomponent was not able to properly parse lists withmultipart/form-dataparser, #644Fixed that not options were passed to
JWToken._build_options, #671
Misc¶
Improved components and auth docs
Version 0.1.0 (2026-03-13)¶
Initial release