Django 3.1: Async Views and JSONField for All
Django 3.1 landed in August 2020. Two headline features: async views finally work, and JSONField is available on all databases.
Async Views
The feature we’ve been waiting for since Django 3.0.
Writing an Async View
import asyncio
from django.http import JsonResponse
async def async_view(request):
# Non-blocking operations
await asyncio.sleep(1)
return JsonResponse({'status': 'ok'})
That’s it. async def instead of def.
Async with External APIs
import httpx
from django.http import JsonResponse
async def fetch_data(request):
async with httpx.AsyncClient() as client:
# Concurrent requests
responses = await asyncio.gather(
client.get('https://api1.example.com/data'),
client.get('https://api2.example.com/data'),
)
data = [r.json() for r in responses]
return JsonResponse({'results': data})
What’s Still Sync
The ORM is still synchronous. Use sync_to_async:
from asgiref.sync import sync_to_async
from django.http import JsonResponse
@sync_to_async
def get_users():
return list(User.objects.all())
async def user_list(request):
users = await get_users()
return JsonResponse({'users': [u.username for u in users]})
Running Async Django
# Uvicorn
uvicorn myproject.asgi:application
# Daphne
daphne myproject.asgi:application
# Gunicorn + Uvicorn workers
gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker
When to Use Async Views
✅ External HTTP calls ✅ WebSocket connections ✅ Long-polling ✅ Streaming responses
❌ Database-heavy views (ORM still sync) ❌ CPU-intensive work (use Celery)
JSONField for All Databases
Previously PostgreSQL-only. Now works everywhere.
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
metadata = models.JSONField(default=dict)
settings = models.JSONField(null=True, blank=True)
Database Support
| Database | JSONField Support |
|---|---|
| PostgreSQL | Native JSONB |
| MySQL 5.7+ | Native JSON |
| SQLite 3.9+ | TEXT with JSON functions |
| Oracle | CLOB with JSON functions |
Querying
# Works on all databases
Product.objects.filter(metadata__key="value")
Product.objects.filter(settings__theme="dark")
# PostgreSQL-only
Product.objects.filter(metadata__has_key="dimensions")
Product.objects.filter(metadata__contains={"active": True})
Migration from postgres.fields
# Before
from django.contrib.postgres.fields import JSONField
# After
from django.db import models
# Use models.JSONField instead
Other Notable Changes
Async Middleware
class AsyncMiddleware:
async_capable = True
sync_capable = True
def __init__(self, get_response):
self.get_response = get_response
async def __call__(self, request):
# Async processing
response = await self.get_response(request)
return response
Cross-Database JSONField Indexes
from django.db import models
from django.contrib.postgres.indexes import GinIndex
class Product(models.Model):
metadata = models.JSONField(default=dict)
class Meta:
indexes = [
# PostgreSQL
GinIndex(fields=['metadata']),
]
PathInfo Improvements
from django.urls import path
urlpatterns = [
path('users/<int:user_id>/', views.user_detail),
]
# In view
def user_detail(request, user_id):
# user_id is now typed as int, not str
Admin Improvements
- Dark mode support (follows browser preference)
- Improved sidebar navigation
- Better mobile experience
Upgrade Path
From Django 3.0:
- Update requirements:
django>=3.1,<3.2 - Replace
django.contrib.postgres.fields.JSONFieldwithdjango.db.models.JSONField - Test async compatibility if using async views
- Run migrations
Breaking changes minimal for 3.0 → 3.1.
Performance Considerations
Async Overhead
Async has overhead. For simple views:
# This might be slower due to async overhead
async def simple_view(request):
return JsonResponse({'ok': True})
# Sync is fine for no-I/O views
def simple_view(request):
return JsonResponse({'ok': True})
Use async when you have actual async work.
JSONField Indexing
# For PostgreSQL - significant query speedup
class Meta:
indexes = [
GinIndex(fields=['metadata']),
]
# For others - consider generated columns or separate tables
# for frequently queried JSON keys
The Async Roadmap
Django 3.1 is a step, not the destination:
- 3.1: Async views, async middleware
- 3.2 LTS: Consolidation
- 4.0: Start of async ORM work
- 4.1+: Async ORM queries
Full async Django is a multi-year project.
Final Thoughts
Django 3.1 delivers on the async promise begun in 3.0. Async views work. JSONField is universal.
For most projects, the upgrade is smooth. Start using async views for external API calls. Enjoy JSONField without PostgreSQL requirement.
The async future is arriving, one release at a time.
Django keeps evolving. Your stack should too.