Async Django REST Framework
ADRF (Async Django REST Framework) is a Python library that provides asynchronous support for Django REST framework. It allows developers to write async class and function-based views, viewsets, and serializers, leveraging Django's native async capabilities (Django 4.1+). The library is currently at version 0.1.12 and maintains an active release cadence with regular updates and bug fixes.
Warnings
- breaking All handler methods in `adrf` class-based views and viewsets must be asynchronous (e.g., `async def get(self, request):`). Synchronous handlers will raise exceptions in an async context.
- gotcha When using `ModelViewSet` with `adrf`, you must use `adrf.routers.DefaultRouter` instead of `rest_framework.routers.DefaultRouter`. The default DRF router will not correctly map to `adrf`'s async CRUD actions (e.g., `aretrieve`, `alist`).
- gotcha Calling synchronous Django ORM operations directly from an async context can lead to `SynchronousOnlyOperation` errors. While `adrf` wraps many common ORM operations, custom or complex synchronous database calls still require explicit handling.
- gotcha `adrf` officially supports Django 4.1 and above. Using it with older Django versions may lead to unexpected behavior or missing async features, as Django's native async support was introduced and matured in these versions.
Install
-
pip install adrf -
# In your Django project's settings.py INSTALLED_APPS = [ # ... 'rest_framework', 'adrf', # ... ]
Imports
- APIView
from adrf.views import APIView
- api_view
from adrf.decorators import api_view
- ListCreateAPIView
from adrf.generics import ListCreateAPIView
- ModelViewSet
from adrf.viewsets import ModelViewSet
- Router
from adrf.routers import DefaultRouter
Quickstart
import os
import django
from django.conf import settings
from django.urls import path
from rest_framework.response import Response
from adrf.views import APIView
from adrf.decorators import api_view
from adrf.generics import ListCreateAPIView
from adrf.viewsets import ModelViewSet
from adrf.routers import DefaultRouter
from rest_framework import serializers
# Minimal Django settings for a runnable example
settings.configure(
DEBUG=True,
SECRET_KEY=os.environ.get('DJANGO_SECRET_KEY', 'a-very-secret-key'),
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.contenttypes',
'rest_framework',
'adrf',
'adrf_app' # A dummy app for models/serializers
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}},
ROOT_URLCONF=__name__,
TEMPLATES=[{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
}],
ASGI_APPLICATION='adrf_app.asgi.application' # Important for async
)
# Create a dummy app for models/serializers
# In a real project, this would be a separate app directory
class AdrfAppConfig(django.apps.AppConfig):
name = 'adrf_app'
label = 'adrf_app'
verbose_name = 'ADRF App'
settings.INSTALLED_APPS.append(AdrfAppConfig.name)
django.setup()
# Dummy Model
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=100)
value = models.IntegerField()
def __str__(self):
return self.name
# Dummy Serializer
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = '__all__'
# Async Class-Based View
class AsyncItemView(APIView):
async def get(self, request, *args, **kwargs):
await Item.objects.acreate(name="Fetched Item", value=100) # Example async ORM
return Response({"message": "This is an async class-based view!"})
# Async Function-Based View
@api_view(['GET'])
async def async_function_view(request):
await Item.objects.acreate(name="Function Item", value=200)
return Response({"message": "This is an async function-based view!"})
# Async Generic View
class AsyncItemListView(ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
async def get_queryset(self): # Override to ensure async ORM
return await super().get_queryset().filter(value__gt=50)
async def aperform_create(self, serializer):
await serializer.asave() # Use async save
# Async ModelViewSet
class AsyncItemViewSet(ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
# adrf's ModelViewSet automatically wraps common ORM operations in sync_to_async
# but you can override specific async methods like 'alist' or 'aretrieve'
async def alist(self, request, *args, **kwargs):
await Item.objects.acreate(name="ViewSet List Item", value=300)
return await super().alist(request, *args, **kwargs)
# Setup URLs
router = DefaultRouter()
router.register(r'items-viewset', AsyncItemViewSet)
urlpatterns = [
path('async-class-view/', AsyncItemView.as_view()),
path('async-function-view/', async_function_view),
path('async-generic-list/', AsyncItemListView.as_view()),
] + router.urls
# To run this example (requires ASGI server like uvicorn):
# 1. Save this code as e.g., `my_adrf_app.py`
# 2. Run from your terminal: `uvicorn my_adrf_app:urlpatterns --reload` (or specify an ASGI app entry point if using a real project structure)
# 3. Access in browser: http://127.0.0.1:8000/async-class-view/