You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+18-25Lines changed: 18 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,12 +1,8 @@
1
1
# django-fqq
2
2
3
-
Lightweight PostgreSQL schema-based multi-tenancy for Django.
3
+
Generate schema-prefixed table names with the Django ORM.
4
4
5
-
django-fqq automatically qualifies table names with the appropriate PostgreSQL schema, routing queries to tenant-specific schemas using a simple `ContextVar`-based approach. Shared tables (like `auth`) stay in `public`, while tenant tables are directed to the active schema.
6
-
7
-
## How it works
8
-
9
-
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 `"tenant_schema"."my_table"` automatically. No changes to your models or queries required.
5
+
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.
10
6
11
7
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 "").
12
8
@@ -39,9 +35,9 @@ INSTALLED_APPS = [
39
35
]
40
36
```
41
37
42
-
### 3. Configure shared apps
38
+
### 3. Configure shared apps (optional)
43
39
44
-
Apps listed in `FQQ_SHARED_APPS` will always use the `public` schema. Everything else uses the active tenant schema.
40
+
Apps listed in `FQQ_SHARED_APPS` will always use the `public` schema, regardless of the active schema.
45
41
46
42
```python
47
43
FQQ_SHARED_APPS= ["auth", "contenttypes"]
@@ -56,7 +52,7 @@ MIDDLEWARE = [
56
52
]
57
53
```
58
54
59
-
Subclass `SchemaMiddleware` and override `_get_schema(request)` to resolve the tenant schema from the request (e.g. from subdomain, header, or URL).
55
+
Subclass `SchemaMiddleware` and override `_get_schema(request)` to resolve the schema from the request (e.g. from subdomain, header, or URL).
60
56
61
57
## Usage
62
58
@@ -65,8 +61,8 @@ Subclass `SchemaMiddleware` and override `_get_schema(request)` to resolve the t
65
61
```python
66
62
from django_fqq.schema import set_schema, clear_schema
67
63
68
-
set_schema("tenant_abc")
69
-
# All queries now target the "tenant_abc" schema
64
+
set_schema("my_schema")
65
+
# All queries now target the "my_schema" schema
70
66
# ...
71
67
clear_schema()
72
68
```
@@ -78,43 +74,40 @@ from django_fqq.middleware import SchemaMiddleware
78
74
79
75
classMySchemaMiddleware(SchemaMiddleware):
80
76
def_get_schema(self, request):
81
-
# Resolve tenant from subdomain
82
77
host = request.get_host().split(".")[0]
83
78
return host
84
79
```
85
80
86
81
### Using a context manager
87
82
88
-
`query_schema`is a context manager that sets the schema for the duration of a block and automatically restores the previous state on exit. If a schema was already active, it is restored rather than cleared.
83
+
`query_schema` sets the schema for the duration of a block and restores the previous state on exit.
89
84
90
85
```python
91
86
from django_fqq.schema import query_schema
92
87
93
-
with query_schema("tenant_abc"):
94
-
# All queries target "tenant_abc"
88
+
with query_schema("my_schema"):
89
+
# All queries target "my_schema"
95
90
...
96
91
# Schema is restored to its previous value (or cleared if none was set)
97
92
```
98
93
99
-
It supports nesting — each level restores the schema that was active before it:
94
+
Nesting is supported — each level restores the schema that was active before it:
100
95
101
96
```python
102
97
from django_fqq.schema import set_schema, query_schema
103
98
104
-
set_schema("tenant_a")
99
+
set_schema("schema_a")
105
100
106
-
with query_schema("tenant_b"):
107
-
# queries target "tenant_b"
108
-
with query_schema("tenant_c"):
109
-
# queries target "tenant_c"
101
+
with query_schema("schema_b"):
102
+
# queries target "schema_b"
103
+
with query_schema("schema_c"):
104
+
# queries target "schema_c"
110
105
...
111
-
# back to "tenant_b"
106
+
# back to "schema_b"
112
107
113
-
# back to "tenant_a"
108
+
# back to "schema_a"
114
109
```
115
110
116
-
The previous schema is also restored if an exception is raised inside the block.
117
-
118
111
### Context-safe
119
112
120
113
Schema state is stored in a `ContextVar`, so it's safe across concurrent async requests and threads.
0 commit comments