Django
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.
Quick start
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
Choose between ASGI and WSGI
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.
Serve a WSGI application
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"].
Serve an ASGI application
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"].
Configure Django settings
Pass secrets
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.
Use Cloudflare storage as Django backends
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",
]Database backends
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.
Transaction support
The D1 and Durable Object backends do not support transactions.
from django.db import transaction
# This will be a no-op, and rollback will not work
@transaction.atomic
def my_view():
# ...D1 backend
To use D1 as a database backend, first setup your D1 database in Wrangler:
wrangler.jsoncwrangler.toml
{
"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)Durable Objects backend
To use Durable Objects as a database backend, first setup your Durable Objects binding in Wrangler:
wrangler.jsoncwrangler.toml
{
"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)More examples
Clone the cloudflare/python-workers-examples repository and run Django examples: