Run a Django backend (Django ORM, Django Admin) alongside a Reflex app.
Project description
reflex-django
Table of contents
About reflex-django
reflex-django is a Reflex plugin that runs a Django ASGI application and your Reflex app in one process under a single dev command (reflex run). HTTP requests whose paths match configured prefixes (for example Django Admin, optional API routes, and path-based static URLs) are forwarded to Django. Everything else—including the compiled Reflex frontend and Reflex’s Socket.IO event channel under /_event/…—is handled by Reflex.
Why it exists. Reflex gives you a Python-first reactive UI. Django gives you the ORM, migrations, the admin, sessions, authentication, internationalization, and the ecosystem of HTTP views and middleware you already rely on. reflex-django lets you keep that Django surface area without standing up a separate HTTP server for local development or simple deployments, while still using Reflex for the interactive UI.
Why Django developers need it. Reflex user actions arrive over WebSocket events, not through Django’s normal request/response cycle, so Django’s HTTP middleware (sessions, auth, locale) does not run for those events by default. reflex-django adds an explicit event bridge that rebuilds a synthetic HttpRequest from the browser data Reflex provides, attaches the session and user, and exposes that request through small APIs your event handlers can call. A separate HTTP bridge routes ordinary browser HTTP traffic on selected path prefixes to Django’s ASGI app. Together, these bridges make Django session auth and related patterns usable from Reflex without pretending the two stacks share one router.
Author. This package is written by Mohannad Irshedat.
| Topic | Versions |
|---|---|
| Django | 6.0.x |
| Python | 3.12+ |
Architecture
At a high level, reflex-django does three coordinated things:
-
Plugin bootstrap — When
rxconfig.pyis loaded,ReflexDjangoPluginsetsDJANGO_SETTINGS_MODULE(if you passsettings_module), exports path prefix environment variables used by Django settings, and callsconfigure_django()so Django is initialized before your Reflex app module imports models or translation helpers. -
HTTP ASGI composition — After Reflex compiles your app, the plugin appends an
api_transformerthat wraps Reflex’s inner ASGI app. Incoming HTTP (and WebSocket upgrade traffic where applicable) is dispatched by URL path prefix: matching prefixes go to Django’s ASGI application (optionally wrapped for static files); non-matching traffic stays on Reflex. ASGI lifespan events are owned by Reflex. -
Per-event Django context — When
install_event_bridgeis true (the default), Reflex registersDjangoEventBridgemiddleware. On each Reflex event, the bridge builds a syntheticdjango.http.HttpRequestfromevent.router_data(cookies, headers, client IP, path), loads the Django session, optionally applies the same locale negotiation asLocaleMiddleware, resolvesrequest.uservia Django’s asyncaget_user, and binds that request on a context variable for the duration of the event. Your handlers callcurrent_user(),current_request(), and related helpers, which read that binding.
PyPI and other indexes do not resolve relative image paths in the project description. Use an absolute URL (below) and keep img.png on your GitHub default branch, or change the URL to match your fork and branch name.
ASCII overview (same idea everywhere, including plain-text viewers):
Browser
│ HTTP (paths under admin / api / static …)
├──────────────────────────────► Django ASGI
│
│ HTTP + Socket.IO (Reflex UI, /_event/ …)
└──────────────────────────────► Reflex ASGI (prefix dispatcher)
│
▼
Reflex event → DjangoEventBridge
→ contextvars (current_request, …)
→ your @rx.event handlers
How to set up
Use uv to create a project, add dependencies, scaffold the Reflex frontend, create a Django project, then wire the plugin in rxconfig.py.
-
Initialize a Python project:
uv init -
Add Reflex and reflex-django:
uv add reflex reflex-django
-
Initialize the Reflex frontend (app name and layout follow Reflex’s CLI):
uv run reflex init frontend
-
Create a Django project package named
backend(adjust the name if you prefer):uv run django-admin startproject backend .
This typically produces
backend/settings.py,backend/urls.py, andmanage.pyin the current directory. -
Configure
rxconfig.pyso Reflex loads Django with your settings module. Import the plugin and passsettings_moduleas the dotted path to your Django settings (for examplebackend.settings):import reflex as rx from reflex_django import ReflexDjangoPlugin config = rx.Config( app_name="myapp", plugins=[ ReflexDjangoPlugin(settings_module="backend.settings"), ], )
Minimal Django expectations. Your INSTALLED_APPS should include the Django contrib apps you need (for example django.contrib.auth, django.contrib.sessions, and often django.contrib.admin) and reflex_django if you use bundled helpers. Set ROOT_URLCONF and mount admin (and any HTTP routes under an optional backend_prefix) so path-based routing matches what you configure on ReflexDjangoPlugin. Run the app with:
uv run reflex run
Commands
reflex-django registers a django command group on the reflex CLI (via a small import hook installed with the package) and also ships a standalone console script reflex-django.
Running Django management commands
-
Through Reflex:
uv run reflex django migrate uv run reflex django makemigrations uv run reflex django createsuperuser uv run reflex django shell uv run reflex django collectstatic uv run reflex django help
-
Through the standalone entry point (equivalent forwarding for normal manage.py subcommands):
uv run reflex-django migrate uv run reflex-django help
Any subcommand name other than the special cases below is forwarded to Django’s execute_from_command_line. The wrapper first loads your rxconfig (so ReflexDjangoPlugin can set DJANGO_SETTINGS_MODULE and path prefixes) and then calls configure_django(), so the same settings module Reflex uses at runtime is used for migrations and other management commands.
Init scaffolding (omitted here). reflex django init and reflex-django init exist to scaffold a starter tree but are considered beta; this README does not document them. Prefer the manual flow in How to set up above.
States, context, and bridges
This section walks through Reflex state, Django’s per-event request context (context variables), the two bridges, and the helper states that mirror Django into Reflex UI state—each with a small example.
Reflex rx.State (baseline)
In Reflex, a State class subclasses rx.State. Fields you declare are synchronized with the client where appropriate; values must be JSON-serializable when they cross the wire. You define event handlers with @rx.event (often async def when you call async Django APIs).
import reflex as rx
class CounterState(rx.State):
count: int = 0
@rx.event
def increment(self):
self.count += 1
Per-event Django context (reflex_django.context)
Reflex events do not carry a Django HttpRequest object. reflex-django stores the synthetic request built for the current event on a context variable. Public read helpers:
| Function | Role |
|---|---|
current_request() |
Bound HttpRequest or None outside an event without the bridge |
current_user() |
Django user or AnonymousUser |
current_session() |
Session backend instance or None |
current_language() |
Active language code after locale activation |
Lower-level begin_event_request(request) and end_event_request() are used by the bridge and for tests or advanced scenarios; application code normally relies on the helpers above inside @rx.event handlers.
import reflex as rx
from reflex_django import current_user
class WhoamiState(rx.State):
label: str = ""
@rx.event
async def refresh(self):
user = current_user()
self.label = user.get_username() if user.is_authenticated else "anonymous"
Bridge 1 — HTTP ASGI path dispatcher
ReflexDjangoPlugin installs an api_transformer that wraps Reflex’s ASGI app. Requests whose path starts with any configured prefix are sent to Django’s ASGI app; other paths stay on Reflex. Relevant plugin arguments:
| Argument | Meaning |
|---|---|
backend_prefix |
Optional prefix for your own Django HTTP routes (for example "/api") |
admin_prefix |
Prefix for Django Admin (default "/admin") |
extra_prefixes |
Additional path prefixes routed to Django |
install_event_bridge |
When True, registers DjangoEventBridge (default) |
ReflexDjangoPlugin(
settings_module="backend.settings",
backend_prefix="/api",
admin_prefix="/admin",
extra_prefixes=("/billing",),
)
Your ROOT_URLCONF must define routes under those prefixes so Django can serve them.
Bridge 2 — DjangoEventBridge (Reflex middleware)
DjangoEventBridge runs at the start of each Reflex event. It constructs a synthetic HttpRequest, attaches the session, optionally runs locale negotiation when USE_I18N and REFLEX_DJANGO_I18N_EVENT_BRIDGE allow it, and sets request.user using aget_user. It then calls begin_event_request(request) so current_user() and friends work in your handlers.
To disable this behavior (for example if you do not use Django session auth from Reflex):
ReflexDjangoPlugin(settings_module="backend.settings", install_event_bridge=False)
DjangoUserState
DjangoUserState is a rx.State subclass that mirrors a JSON-safe snapshot of the current user (user_id, username, email, names, is_authenticated, staff/superuser flags, optional group_names). Use it for navbar and conditional UI.
- Call
sync_from_djangofrom a page’son_load(it is an@rx.event). - After login or logout, call
await self.refresh_django_user_fields()from an async handler so the snapshot updates without a full navigation when appropriate.
Server-side authorization must still use current_user() (or stricter checks); client-visible state is for display only.
import reflex as rx
from reflex_django import DjangoUserState
class AuthState(DjangoUserState):
pass
app = rx.App()
# app.add_page(index, on_load=AuthState.sync_from_django)
DjangoI18nState
DjangoI18nState exposes django_language_code and django_language_bidi, aligned with Django’s active language after the event bridge. Use sync_from_django on on_load, or await self.refresh_django_i18n_fields() after the user changes language via a Django-served view.
import reflex as rx
from reflex_django import DjangoI18nState
app = rx.App()
# app.add_page(home, on_load=DjangoI18nState.sync_from_django)
Reflex-facing Django context (processors and DjangoContextState)
Reflex serializes state to the browser, so any dict you merge into Reflex state must be JSON-serializable.
Two configuration modes (see reflex_django.reflex_context):
-
REFLEX_DJANGO_CONTEXT_PROCESSORS— A non-empty tuple of dotted paths to callables(request) -> dict(or async). When this list is set, it is used exclusively. You are responsible for JSON-safe values. -
Template context processors — If
REFLEX_DJANGO_CONTEXT_PROCESSORSis empty andREFLEX_DJANGO_USE_TEMPLATE_CONTEXT_PROCESSORSisTrue, the same dotted paths as inTEMPLATES[*].OPTIONS["context_processors"]are run, with built-in sanitization (for exampleuserbecomes a snapshot;request,perms,messagesare omitted; other values must pass plainjson.dumps).
Built-in processor callables you can list explicitly:
reflex_django.reflex_context.builtin_user_context— adds a template-shapeduserkey as a snapshot dict.reflex_django.reflex_context.builtin_i18n_context— addsLANGUAGE_CODE,LANGUAGE_BIDI, andLANGUAGES.
Example settings snippet:
REFLEX_DJANGO_CONTEXT_PROCESSORS = (
"reflex_django.reflex_context.builtin_user_context",
"reflex_django.reflex_context.builtin_i18n_context",
)
collect_reflex_context(request) runs the configured processor list and returns one merged dict. You can await it inside a handler when current_request() is bound:
from reflex_django import current_request
from reflex_django.reflex_context import collect_reflex_context
@rx.event
async def debug_context(self):
merged = await collect_reflex_context(current_request())
# use merged (e.g. assign JSON-safe keys to state)
DjangoContextState holds django_context and a stringified django_context_json. Call load_django_context from on_load to populate them from processors using the bound request:
from reflex_django import DjangoContextState
# app.add_page(about, on_load=DjangoContextState.load_django_context)
Helper functions template_context_processor_paths() and reflex_context_processor_paths() introspect settings for tooling or documentation.
user_snapshot vs current_user
user_snapshot(user) returns the same flat dict shape used by DjangoUserState, given a user instance. It is useful in custom context processors, tests, or logging—without touching Reflex state.
current_user() returns the live Django user (or anonymous) for the current Reflex event after the bridge runs. Use it for permissions, ownership checks, and mutations on the server.
DjangoUserState fields are a UI snapshot; always re-check authorization with current_user() (or equivalent) inside event handlers that change data.
from reflex_django import current_user
from reflex_django.auth_state import user_snapshot
@rx.event
async def audit_banner(self):
self.snapshot_json = user_snapshot(current_user()) # dict for display
assert current_user().is_authenticated # real check for protected actions
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reflex_django-0.1.1.tar.gz.
File metadata
- Download URL: reflex_django-0.1.1.tar.gz
- Upload date:
- Size: 49.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41f8d3ce33d4a8611e7e9ffba0fd6db6cc5657383195a14352ab333d7658fd7d
|
|
| MD5 |
652e397533d2a0dfec6b7706231a595a
|
|
| BLAKE2b-256 |
0030672af55c8770d7d9fd93bbf4d326411d35d8c05028db247b6fb3281011da
|
File details
Details for the file reflex_django-0.1.1-py3-none-any.whl.
File metadata
- Download URL: reflex_django-0.1.1-py3-none-any.whl
- Upload date:
- Size: 47.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f0d1a311242898e3e0d682840c26e01f1b6758124449af97f6b6d5044d09b28
|
|
| MD5 |
21205aebee6ca1cebfc3df088c180c42
|
|
| BLAKE2b-256 |
a449861b65c76cb46c610949940d13d66bf512b5b0a2a846b1c5f0e2c023adf5
|