Generate schema-prefixed table names with the Django ORM.
django-fqq provides a custom database backend that wraps Django's built-in PostgreSQL backend. It overrides quote_name to prepend the active schema to table names — so "my_table" becomes "my_schema"."my_table" automatically. No changes to your models or queries required.
django-fqq does not modify queries if set_schema has not been called or has been set to a falsy value (e.g. None or "").
pip install django-fqqDATABASES = {
"default": {
"ENGINE": "django_fqq.backend",
"NAME": "your_db",
# ... other options
}
}If you need a custom database backend you can either inherit from django_fqq.backend.base.DatabaseWrapper or set ops_class = DatabaseOperations from django_fqq.backend.operations.DatabaseOperations in your DatabaseWrapper.
INSTALLED_APPS = [
# ...
"django_fqq",
]Apps listed in FQQ_SHARED_APPS will always use the public schema, regardless of the active schema.
FQQ_SHARED_APPS = ["auth", "contenttypes"]The middleware provides very basic routing of requests to a specific database schema. For a more robust solution that handles migrations and other functionality, check out django-tenants.
MIDDLEWARE = [
"django_fqq.middleware.SchemaMiddleware",
# ...
]Subclass SchemaMiddleware and override _get_schema(request) to resolve the schema from the request (e.g. from subdomain, header, or URL).
from django_fqq.schema import set_schema, clear_schema
set_schema("my_schema")
# All queries now target the "my_schema" schema
# ...
clear_schema()from django_fqq.middleware import SchemaMiddleware
class MySchemaMiddleware(SchemaMiddleware):
def _get_schema(self, request):
host = request.get_host().split(".")[0]
return hostquery_schema sets the schema for the duration of a block and restores the previous state on exit.
from django_fqq.schema import query_schema
with query_schema("my_schema"):
# All queries target "my_schema"
...
# Schema is restored to its previous value (or cleared if none was set)Nesting is supported — each level restores the schema that was active before it:
from django_fqq.schema import set_schema, query_schema
set_schema("schema_a")
with query_schema("schema_b"):
# queries target "schema_b"
with query_schema("schema_c"):
# queries target "schema_c"
...
# back to "schema_b"
# back to "schema_a"Schema state is stored in a ContextVar, so it's safe across concurrent async requests and threads.
Requires Python 3.12+ and Django 4.2+. Tested against Django 4.2, 5.2, and 6.0.
uv sync
uv run pytestMIT