Routing is the process of deciding to which tenant an incoming request belongs, and activating it for the rest of the request/response cycle. This is typically done via middleware and this package provides three routing mechanisms: domain, header and session routing.
The goal of these middleware is to augment the request
object with tenant information. When these middleware are used the request
will contain a tenant
property with an instance of either the tenant model or the class django_pgschemas.schema.Schema
.
Domain routing¶
Tenants will have one or many domains (or subdomains), but each domain will correspond to only one tenant.
In this mechanism we use a database table to control domains per tenant. This domain model can be defined like this:
from django_pgschemas.models import DomainModel
class Domain(DomainModel):
pass
And added to the tenant settings like this:
TENANTS = {
"public": {
"APPS": [
"django.contrib.contenttypes",
"django.contrib.staticfiles",
"django_pgschemas",
"tenants",
],
},
"www": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"main",
],
"DOMAINS": ["mydomain.com"],
"URLCONF": "main.urls",
},
"blog": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"blog",
],
"DOMAINS": ["blog.mydomain.com", "help.mydomain.com"],
"URLCONF": "blog.urls",
},
"default": {
"TENANT_MODEL": "tenants.Tenant",
"DOMAIN_MODEL": "tenants.Domain",
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"customers",
],
"URLCONF": "customers.urls",
}
}
Then you can assign domains to tenants:
>>> from tenants.models import Tenant, Domain
>>> tenant = Tenant.objects.create(schema_name="tenant_1")
>>> Domain.objects.create(tenant=tenant, domain="tenant1.mydomain.com")
Note
Notice that the public
schema doesn't have DOMAINS
configured. This is intentional. Attempting to add this key would result in an ImproperlyConfigured
error. The public schema is non-routable by design.
Finally add DomainRoutingMiddleware
to the top of the middleware stack, so that all subsequent middleware can benefit from the added tenant.
MIDDLEWARE = (
"django_pgschemas.routing.middleware.DomainRoutingMiddleware",
# other middleware
)
Subfolder routing¶
It is also possible to use subfolder routing, instead of using domains/subdomains. In this case all tenants would share the same domain, but with a different "folder" component at the beginning of the requested path. The domain model supports this by default and allows for multiple combinations:
>>> from tenants.models import Tenant, Domain
>>> tenant = Tenant.objects.create(schema_name="tenant_1")
>>> Domain.objects.create(
... tenant=tenant,
... domain="tenant1.mydomain.com",
... is_primary=True,
... )
>>> Domain.objects.create(
... tenant=tenant,
... domain="tenants.mydomain.com",
... folder="tenant1",
... )
Warning
Subfolder routing is currently not supported for static tenants.
For a special case with subfolder routing please see fallback domains.
Header routing¶
In this mechanism a request header is defined to pass the tenant database ID or the schema name.
PGSCHEMAS_TENANT_HEADER = "tenant"
Static tenants can be routed using the HEADER
key in the TENANTS
settings:
TENANTS = {
"public": {
"APPS": [
"django.contrib.contenttypes",
"django.contrib.staticfiles",
"django_pgschemas",
"tenants",
],
},
"www": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"main",
],
"HEADER": "main",
"URLCONF": "main.urls",
},
"blog": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"blog",
],
"HEADER": "blog",
"URLCONF": "blog.urls",
},
"default": {
"TENANT_MODEL": "tenants.Tenant",
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"customers",
],
"URLCONF": "customers.urls",
}
}
Note
Notice that the public
schema doesn't have HEADER
configured. This is intentional. Attempting to add this key would result in an ImproperlyConfigured
error. The public schema is non-routable by design.
Then add HeadersRoutingMiddleware
to the top of the middleware stack.
MIDDLEWARE = (
"django_pgschemas.routing.middleware.HeadersRoutingMiddleware",
# other middleware
)
Session routing¶
In this mechanism a session key is defined to store the tenant database ID or the schema name.
PGSCHEMAS_TENANT_SESSION_KEY = "tenant"
Static tenants can be routed using the SESSION_KEY
key in the TENANTS
settings:
TENANTS = {
"public": {
"APPS": [
"django.contrib.contenttypes",
"django.contrib.staticfiles",
"django_pgschemas",
"tenants",
],
},
"www": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"main",
],
"SESSION_KEY": "main",
"URLCONF": "main.urls",
},
"blog": {
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"blog",
],
"SESSION_KEY": "blog",
"URLCONF": "blog.urls",
},
"default": {
"TENANT_MODEL": "tenants.Tenant",
"APPS": [
"django.contrib.auth",
"django.contrib.sessions",
"customers",
],
"URLCONF": "customers.urls",
}
}
Note
Notice that the public
schema doesn't have SESSION_KEY
configured. This is intentional. Attempting to add this key would result in an ImproperlyConfigured
error. The public schema is non-routable by design.
Then add SessionRoutingMiddleware
to the top of the middleware stack, but after the session middleware.
MIDDLEWARE = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django_pgschemas.routing.middleware.SessionRoutingMiddleware",
# other middleware
)