Skip to content

Database

Four ways to load and save Django data in Reflex handlers. Pick by how much boilerplate you want.

1. Manual async ORM

Full control. Build dicts yourself. Good for learning and one-off logic.

Avoid Use
Model.objects.get(...) await Model.objects.aget(...)
Model.objects.create(...) await Model.objects.acreate(...)
instance.save() await instance.asave()
list(qs) [row async for row in qs[:50]]
class CatalogState(AppState):
    products: list[dict] = []

    @rx.event
    async def load(self):
        from shop.models import Product
        self.products = [
            {"id": p.id, "name": p.name}
            async for p in Product.objects.all()[:50]
        ]

See the Tutorial for a full CRUD example.

2. Serializers

Same handlers, less field plumbing. Declarative Meta.fields and .adata() / .alist().

Serializers

3. Model state

Declarative CRUD: declare model and fields, get list/search/pagination/save/create/delete handlers.

Model state

4. Live lists

For lists that should react to model changes from other tabs or requests, add LiveListMixin to a ModelState and start live_subscribe from page load.

Live updates

Rules for all approaches

  • Store dicts in state, not model instances.
  • Filter by owner in handlers: await Todo.objects.aget(pk=id, owner=self.request.user).
  • Wrap blocking libraries in sync_to_async when no async API exists.

Comparison

Manual ORM Serializers ModelState LiveListMixin
Boilerplate Most Medium Least Low
Custom logic Easiest Easy Override handlers Override queryset/serializer
List + forms You build it You build UI Built-in vars Patches current list