Async Views & the ORM

Write async def views for I/O-bound work, and use the ORM's async methods without shooting yourself in the foot.

Django 5.x has mature async support. An async def view runs on the ASGI stack and can await multiple I/O operations concurrently — calling three external APIs in the time one would take. The win is real, but only for I/O-bound work; async does nothing for CPU-bound code and adds complexity, so reach for it deliberately.

What's safe and what isn't

import httpx
from django.http import JsonResponse

async def dashboard(request):
    async with httpx.AsyncClient() as client:
        r = await client.get('https://api.example.com/stats')
    return JsonResponse(r.json())

The ORM is synchronous under the hood, so it exposes a-prefixed async methods: aget(), acreate(), afirst(), acount(), and async for to iterate a queryset. You cannot call the normal blocking versions directly in an async view — Django raises SynchronousOnlyOperation.

Need to call a chunk of sync code (a library, some legacy ORM logic) from async? Wrap it in sync_to_async(fn)(). Going the other way, call async code from sync with async_to_sync. These adapters from asgiref are the bridge between the two worlds.

Example

Example · python
import asyncio
import httpx
from django.http import JsonResponse
from asgiref.sync import sync_to_async


async def enriched_profile(request, user_id):
    # 1) ORM with async methods — note the 'a' prefixes.
    user = await User.objects.select_related('profile').aget(pk=user_id)

    # 2) Fan out to three slow services concurrently instead of one-by-one.
    async with httpx.AsyncClient(timeout=5) as client:
        billing, activity, geo = await asyncio.gather(
            client.get(f'https://billing/api/users/{user_id}'),
            client.get(f'https://events/api/users/{user_id}/recent'),
            client.get(f'https://geo/api/ip/{request.META["REMOTE_ADDR"]}'),
            return_exceptions=True,   # one failure doesn't sink the others
        )

    # 3) Iterate a queryset asynchronously.
    recent_orders = [
        {'id': o.id, 'total': str(o.total)}
        async for o in Order.objects.filter(user=user).order_by('-created')[:5]
    ]

    # 4) Call unavoidably-sync code from async safely.
    score = await sync_to_async(compute_risk_score)(user)

    return JsonResponse({
        'user': user.username,
        'plan': _safe_json(billing),
        'recent_activity': _safe_json(activity),
        'orders': recent_orders,
        'risk': score,
    })


def _safe_json(response):
    return response.json() if not isinstance(response, Exception) else None

When to use it

  • A developer writes an async view to call an external payment API concurrently with a database lookup, reducing latency.
  • A team migrates a slow view to async def so multiple I/O operations run with asyncio.gather() instead of sequentially.
  • A developer uses Django's async ORM with sync_to_async to fetch data inside an async view safely.

More examples

Simple async view

Defines an async view using async def; Django 4.1+ supports async views natively with ASGI.

Example · python
from django.http import JsonResponse

async def health(request):
    return JsonResponse({"status": "ok"})

Async ORM call with sync_to_async

Wraps a synchronous ORM call in sync_to_async so it can be awaited from within an async view.

Example · python
from django.http import JsonResponse
from asgiref.sync import sync_to_async
from .models import Post

async def post_count(request):
    count = await sync_to_async(Post.objects.count)()
    return JsonResponse({"count": count})

Concurrent I/O with asyncio.gather

Fetches data from two external APIs concurrently using asyncio.gather, halving the total wait time.

Example · python
import asyncio
import httpx
from django.http import JsonResponse

async def dashboard_data(request):
    async with httpx.AsyncClient() as client:
        weather, news = await asyncio.gather(
            client.get("https://api.weather.example/current"),
            client.get("https://api.news.example/top"),
        )
    return JsonResponse({"weather": weather.json(), "news": news.json()})

Discussion

  • Be the first to comment on this lesson.