github wemake-services/django-modern-rest 0.4.0
Version 0.4.0

5 hours ago

AKA "The first version that I enjoy".

Breaking changes

  1. We changed how components are defined in controllers, #738
    Now components will be defined in method parameters, not in base classes.

  2. 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 rules

  3. We removed drm.routing.compose_blueprints function,
    because there no Blueprints anymore :)

  4. We completely changed our SSE and streaming API, see #736
    Old API was removed, new one was introduced.
    dmr.sse package was moved to dmr.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:

  1. Load the latest documentation from https://django-modern-rest.readthedocs.io/llms-full.txt
  2. 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,
        )
  1. Replace all Blueprint and compose_blueprints references with a new API:
    Instead you must use Controller and different methods under a single class
  2. Now, change all @sse-based controllers to new SSEController API, 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}')
  1. Replace old dmr.sse imports with new dmr.streaming.sse alternatives

Features

  • Added @attrs.define official support, #706
  • Added msgpack parser and renderer, #630
  • Added JsonLines or JsonL support, #607
  • Added ping events to SSE streaming, #606
  • Added SSE support for non-GET methods, Body component parsing, #736
  • Added i18n support for user-facing error messages
    using Django's gettext_lazy, #426
  • Added MediaType validation for the default encoding field
    and OpenAPI 3.2 itemEncoding and prefixEncoding fields, #695
  • Added MediaTypeMetadata metadata item to set required parameters
    for the MediaType request body
    for Body and FileMedata components, #695 and #698
  • Added 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_client and
    dmr_async_client, #735.
  • Added Stoplight Elements UI for OpenAPI documentation, #748

Bugfixes

  • Fixed SSE controllers __name__ and __doc__ generation
    via @sse decorator, #700
  • Fixed a bug where FileMetadata rendered list of schemas incorrectly, #698
  • Fixed that we were using typing.get_type_hints in some places,
    now always using typing_extensions.get_type_hints, #768

Misc

  • Added $dmr-openapi-skeleton AI agent skill, #693
  • Added $dmr-from-django-ninja AI agent skill, #693
  • Added $dmr-from-drf AI agent skill, #744
  • Added ETag usage docs, #699
  • Added multiple translations for the user-facing error messages, #718
  • Now MsgspecJsonRenderer and JsonRenderer produce
    the same json string in terms of whitespaces, #736

New Contributors

Full Changelog: 0.3.0...0.4.0

Don't miss a new django-modern-rest release

NewReleases is sending notifications on new releases.