Async Django REST Framework

raw JSON →
0.1.12 verified Wed Apr 15 auth: no python

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.

pip install adrf
error ModuleNotFoundError: No module named 'adrf'
cause The 'adrf' package is not installed in the Python environment.
fix
Install the package using pip: 'pip install adrf'.
error ImportError: cannot import name 'AsyncAPIView' from 'adrf.views'
cause The 'AsyncAPIView' class does not exist in the 'adrf.views' module.
fix
Use the correct import: 'from adrf.views import APIView'.
error TypeError: object NoneType can't be used in 'await' expression
cause An asynchronous method is returning None instead of an awaitable object.
fix
Ensure that the method returns an awaitable object, such as a coroutine or an async function.
error AttributeError: module 'adrf.viewsets' has no attribute 'AsyncModelViewSet'
cause The 'adrf.viewsets' module does not contain an 'AsyncModelViewSet' attribute.
fix
Use the correct import: 'from adrf.viewsets import ViewSet'.
error RuntimeError: You cannot call this from an async context - use a thread or sync_to_async.
cause A synchronous function is being called within an asynchronous context.
fix
Use Django's 'sync_to_async' utility to call the synchronous function within the async context.
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.
fix Ensure all HTTP method handlers (e.g., `get`, `post`, `put`) in `APIView` subclasses and all action methods (e.g., `list`, `create`, `retrieve`, `update`, `destroy`) in `ViewSet`/`ModelViewSet` subclasses are defined with `async def`.
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`).
fix Replace `from rest_framework.routers import DefaultRouter` with `from adrf.routers import DefaultRouter` in your `urls.py`.
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.
fix For any synchronous code that interacts with the Django ORM or other blocking operations within an `async def` function, wrap it using `from asgiref.sync import sync_to_async` (e.g., `await sync_to_async(blocking_function)()`). `adrf` versions 0.1.10 and 0.1.11 included fixes for pagination and general ORM operations, but vigilance is still required for custom logic.
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.
fix Ensure your Django project is running Django 4.1 or a newer version. It is also recommended to use the latest patch release of your chosen Django series.
# In your Django project's settings.py INSTALLED_APPS = [ # ... 'rest_framework', 'adrf', # ... ]

This quickstart demonstrates setting up `adrf` with async class-based views, function-based views, generic views, and a ModelViewSet. It includes a basic Django setup, a dummy model, and a serializer. Note the use of `async def` for view methods and `adrf`'s specific router for ModelViewSets. To run, save as a Python file and serve with an ASGI server like Uvicorn (e.g., `uvicorn your_file_name:urlpatterns --reload`).

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/