Django ↗ is supported in Python Workers.
Django applications use protocols called the Web Server Gateway Interface (WSGI) ↗ or Asynchronous Server Gateway Interface (ASGI) ↗.
This means that Django never reads from or writes to a socket itself. A WSGI/ASGI application expects to be hooked up to a WSGI/ASGI server, such as uvicorn ↗. The WSGI/ASGI server handles all of the raw sockets on the application’s behalf.
Python Workers provide adaptors for both WSGI and ASGI, so you can choose any based on whether your Django application deploys to WSGI or ASGI.
To get started with Django in Python Workers, follow these steps:
-
Create a Django project using
pywrangler init:uv run pywrangler init django-worker --template https://github.com/cloudflare/python-workers-examples/tree/main/django cd django-worker -
Run your worker locally:
uv run pywrangler dev
Your Django application needs to be served using either ASGI or WSGI. While Python workers is optimized for ASGI, you can still use WSGI which is compatible with Django.
Build the application object with get_wsgi_application() and pass it to workers.wsgi.fetch:
import os
from django.core.wsgi import get_wsgi_application
from workers import wsgi
# your Django settings module
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
app = get_wsgi_application()
Default = wsgi.entrypoint(app)wsgi.fetch takes the application object, the incoming request, and the environment.
It exposes your bindings to the application through scope["env"].
Build the application object with get_asgi_application() and pass it to workers.asgi.fetch:
import os
from django.core.asgi import get_asgi_application
from workers import asgi
# your Django settings module
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
app = get_asgi_application()
Default = asgi.entrypoint(app)asgi.fetch takes the application object, the incoming request, and the environment.
It exposes your bindings to the application through scope["env"].
If you need a secret (like SECRET_KEY) in your Django settings, you can read it from a Worker secret:
from workers import env
SECRET_KEY = env.DJANGO_SECRET_KEYCreate the secret with uv run pywrangler secret put DJANGO_SECRET_KEY.
You can use Cloudflare D1 and Durable Objects as Django database backends.
To use them, you need to install the django-cf ↗ package.
Add django-cf to your dependencies:
[project]
dependencies = [
"django",
"django-cf",
]django-cf provides two SQLite-compatible backends using Cloudflare's D1 and Durable Objects.
Both drive the synchronous Django ORM, so serve your application through the WSGI path when you use them.
To use D1 as a database backend, first setup your D1 database in Wrangler:
{
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database",
"database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
]
}[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"Then, configure the backend in your Django settings:
DATABASES = {
"default": {
"ENGINE": "django_cf.db.backends.d1",
# should match the binding name in your wrangler.jsonc
"CLOUDFLARE_BINDING": "DB",
}
}You are all set. Your Django application now uses D1 as its database backend.
import os
from django.core.wsgi import get_wsgi_application
from workers import WorkerEntrypoint, wsgi
# your Django settings module
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
application = get_wsgi_application()
class Default(WorkerEntrypoint):
async def fetch(self, request):
return await wsgi.fetch(application, request, self.env)To use Durable Objects as a database backend, first setup your Durable Objects binding in Wrangler:
{
"durable_objects": {
"bindings": [
{
"name": "DO_STORAGE",
"class_name": "DjangoDurableObject"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["DjangoDurableObject"]
}
]
}[[durable_objects.bindings]]
name = "DO_STORAGE"
class_name = "DjangoDurableObject"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "DjangoDurableObject" ]Then, configure the backend in your Django settings:
DATABASES = {
"default": {
"ENGINE": "django_cf.db.backends.do",
}
}Then, update your Python worker as follows:
import os
from django.core.wsgi import get_wsgi_application
from django_cf.db.backends.do.storage import set_storage
from workers import WorkerEntrypoint, DurableObject, wsgi
# your Django settings module
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
application = get_wsgi_application()
class DjangoDurableObject(DurableObject):
def __init__(self, ctx, env):
super().__init__(ctx, env)
# Tell Django to use the Durable Object storage
set_storage(self.ctx.storage.sql)
async def fetch(self, request):
return await wsgi.fetch(application, request, self.env)
class Default(WorkerEntrypoint):
async def fetch(self, request):
id = self.env.DO_STORAGE.idFromName("my-do-backend")
stub = self.env.DO_STORAGE.get(id)
return await stub.fetch(request)Clone the cloudflare/python-workers-examples repository and run Django examples: