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.
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 will be made.
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
Fixes¶
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 continous 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, #698Fixed that we were using
typing.get_type_hintsin some places, now always usingtyping_extensions.get_type_hints, #768
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