I spent four years treating the backend as somebody else's problem. Then I needed an API that nobody else was going to build.
Why Django and not something lighter
I wanted the admin. Not a framework with an admin plugin — the real one, where a model gets a usable CRUD interface the moment you register it. For a one-person project that is the whole value proposition.
The layering that kept it clean
# apis.py -> selectors.py / services.py -> models.py
def post_list() -> QuerySet[Post]:
return (
Post.objects.filter(status=Post.Status.PUBLISHED)
.select_related("category")
.order_by("-published_at")
)
Views never touch the ORM directly. Reads go through selectors, writes through services. It is more files, and it is worth it the first time a query needs changing in exactly one place.
What surprised me
select_related and prefetch_related are not optimisations you add later. Miss
one on a list endpoint and you turn a single query into one per row. I shipped
that bug once and caught it only because the page took a second to load.
What this site runs on
Django 5.2, DRF, Postgres, and the Unfold admin. It serves the posts, the app listings, and the profile you are reading right now.