layoutEngineering
Batteries-included CRUD: where Django wins, ORM discipline and the N+1 trap, safe migrations, async limits.
1 item
Where Django still wins
When you want batteries included: admin, auth, migrations, ORM, forms, and a mature ecosystem out of the box. For CRUD-heavy products with a real data model and an internal admin need, Django + DRF gets you to production faster than assembling the equivalent from FastAPI parts. This is why vorrei.io's core sits on Django.
ORM discipline — the N+1 problem
The single biggest Django performance trap. Accessing a related object inside a loop fires one query per iteration.
# BAD — 1 query for orders + N queries for each order.client
for order in Order.objects.all():
print(order.client.name)
# GOOD — 2 queries total
for order in Order.objects.select_related("client"):
print(order.client.name)
select_related → SQL JOIN, for forward FK / one-to-one.prefetch_related → separate query + Python join, for reverse FK / many-to-many.django-debug-toolbar or by asserting query counts in tests (assertNumQueries).Query efficiency
.only() / .defer() to limit columns on wide tables..values() / .values_list() when you don't need model instances.bulk_create / bulk_update instead of per-row saves.QuerySet.iterator() for large result sets to avoid loading everything into memory.annotate() + Sum/Count rather than computing in Python.Migrations
Async caveats
sync_to_async, and you don't get true async DB I/O the way you do with SQLAlchemy async.