← ~/logs LOG-006

>Static Islands in a Dynamic Sea

Building typed Python classes on top of Django ORM with Mantle to solve fat models, N+1 queries, and type safety.

Carlton Gibson’s talk at DjangoCon Europe 2026 on building typed layers on top of Django’s ORM using Mantle.

The problem

Django models grow out of control. Display logic, queries, serialization, validation — all crammed into one class. The ORM fetches too much by default and N+1 queries are easy to hit. Static types don’t fit Django’s dynamic core — metaclasses, introspection, and dynamic attributes make type checkers unhappy.

The fix

Build typed dataclasses (using attrs) that define the exact shape of data you need. Mantle bridges from the ORM to those shapes efficiently.

from attrs import define

@define
class BookmarkData:
    url: str
    comment: str
    favourite: bool

This is your “static island” — a plain typed class, no Django magic. Query it with Mantle:

from mantle import Query

bookmarks = Query(Bookmark.objects.all(), BookmarkData).all()
# type: list[BookmarkData]

Mantle generates the ORM query with only() applied — it fetches just the fields you declared. Nothing extra.

@define
class UserData:
    username: str

@define
class BookmarkData:
    url: str
    comment: str
    favourite: bool
    user: UserData

Mantle sees the nested UserData and auto-generates select_related("user") with only("user__username"). Two queries max, regardless of how many bookmarks.

DRF integration

Replace serializers with shape classes:

from mantle_drf.generics import ListCreateAPIView

@attrs.define
class BookmarkShape:
    url: str
    title: str
    comment: str = ""

class BookmarkList(ListCreateAPIView):
    queryset = Bookmark.objects.all()
    shape_class = BookmarkShape  # instead of serializer_class

GET returns shaped data, POST structures incoming JSON into your shape and persists it.

Key takeaways

  • Don’t fight Django’s dynamic nature — build typed layers on top instead
  • Mantle auto-optimizes queries — no manual only(), select_related(), or prefetch_related()
  • Adopt incrementally — start with one view, one shape class
  • DRY applies within concerns, not across them — it’s OK to “repeat” a field name in your model and your data shape

Slides | Experiment code