Async Battery Support
Battery handler methods can be defined with async def. djxi detects this
at class-definition time and wraps them in proper ASGI-compatible view
functions so Django can serve them without blocking the event loop.
Writing an async handler
Use async def for the handler method and await self.arender_section()
for the rendering step:
from djxi import DXEndpointBattery, dx_get, dx_post
class AsyncItemBattery(DXEndpointBattery):
inline_template = """
<dx-section name="item-list">
<ul>{% for item in items %}<li>{{ item.title }}</li>{% endfor %}</ul>
</dx-section>
"""
@dx_get("items/", name="list")
async def list(self, request):
# Django ORM async API
items = [item async for item in Item.objects.order_by("-pk")]
return await self.arender_section(request, "item-list", {"items": items})
@dx_post("items/create", name="create")
async def create(self, request):
title = request.POST.get("title", "").strip()
if title:
await Item.objects.acreate(title=title)
items = [item async for item in Item.objects.order_by("-pk")]
return await self.arender_section(request, "item-list", {"items": items})
Mixing sync and async handlers
A single battery can contain both sync and async methods. djxi generates the correct (sync or async) view wrapper for each decorated method independently:
class MixedBattery(DXEndpointBattery):
inline_template = "..."
@dx_get("light/", name="light")
def light(self, request):
# No async I/O needed; sync is fine
return self.render_section(request, "light")
@dx_get("heavy/", name="heavy")
async def heavy(self, request):
# Needs async database or HTTP calls
result = await some_async_api_call()
return await self.arender_section(request, "heavy", {"result": result})
arender_section()
DXEndpointBattery.arender_section() is an awaitable counterpart to
render_section(). Django’s template rendering is synchronous; this method
wraps it with asgiref.sync.sync_to_async so the event loop is not blocked.
response = await self.arender_section(request, section_name, context)
It accepts the same arguments as render_section():
- DXEndpointBattery.arender_section(self, request, section_name, context=None)
- Parameters:
request – The incoming
HttpRequest.section_name – Name of the
<dx-section>to render.context – Optional dict passed to the template.
- Returns:
Awaitable that resolves to an
HttpResponse.
Permission checks with async handlers
check_permissions() is always synchronous. The async view wrapper calls
it before await-ing the handler, so it works the same way for both sync
and async methods:
class ProtectedAsyncBattery(DXEndpointBattery):
requires_auth = True # works with async handlers too
@dx_get("secure/")
async def secure(self, request):
return await self.arender_section(request, "secure")
Requirements
Django 3.1+ (ASGI support)
asgiref(bundled with Django 3.1+)An ASGI server (e.g.
uvicorn,daphne) for async views to actually run asynchronously in production. Under the WSGI server the view is served synchronously viaasgiref’s thread-pool adapter.
Notes
Template rendering (
django.template.Template.render) is always synchronous.arender_section()runs it in a thread executor viasync_to_asyncso it does not block the event loop.The section cache is class-level and thread-safe (built once, read-only thereafter).