Installation¶
Assuming you have django installed, the first step is to install django-tenants
.
pip install django-tenants
Basic Settings¶
You’ll have to make the following modifications to your settings.py
file.
Your DATABASE_ENGINE
setting needs to be changed to
DATABASES = {
'default': {
'ENGINE': 'django_tenants.postgresql_backend',
# ..
}
}
Add django_tenants.routers.TenantSyncRouter to your DATABASE_ROUTERS setting, so that the correct apps can be synced, depending on what’s being synced (shared or tenant).
DATABASE_ROUTERS = (
'django_tenants.routers.TenantSyncRouter',
)
Add the middleware django_tenants.middleware.main.TenantMainMiddleware
to the top of MIDDLEWARE
, so that each request can be set to use the correct schema.
MIDDLEWARE = (
'django_tenants.middleware.main.TenantMainMiddleware',
#...
)
Make sure you have django.template.context_processors.request
listed under the context_processors
option of TEMPLATES
otherwise the tenant will not be available on request
.
TEMPLATES = [
{
#...
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
#...
],
},
},
]
The Tenant & Domain Model¶
Now we have to create your tenant model. Your tenant model can contain whichever fields you want, however, you must inherit from TenantMixin
. This Mixin only has one field schema_name
which is required. You also have to have a table for your domain names for this you must inherit from DomainMixin
.
Here’s an example, suppose we have an app named customers
and we want to create a model called Client
.
from django.db import models
from django_tenants.models import TenantMixin, DomainMixin
class Client(TenantMixin):
name = models.CharField(max_length=100)
paid_until = models.DateField()
on_trial = models.BooleanField()
created_on = models.DateField(auto_now_add=True)
# default true, schema will be automatically created and synced when it is saved
auto_create_schema = True
class Domain(DomainMixin):
pass
Admin Support¶
TenantAdminMixin is available in order to register the tenant model.
Here’s an example (following the example above), we want to register the Client
model, so we create a the related admin class ClientAdmin
.
The mixin disables save and delete buttons when not in current or public tenant (preventing Exceptions).
from django.contrib import admin
from django_tenants.admin import TenantAdminMixin
from myapp.models import Client
@admin.register(Client)
class ClientAdmin(TenantAdminMixin, admin.ModelAdmin):
list_display = ('name', 'paid_until')
Sub-folder Support¶
Currently in beta.
There is a option that allows you to run Django-Tenants with sub-folder instead of sub-domains.
Note
e.g. http://www.mydomain.local/r/schemaname/ instead of http://schemaname.mydomain.local/
Warning
The schemaname
value from the URL path has to match domain
value which is set when creating a new tenant, as described https://django-tenants.readthedocs.io/en/latest/use.html#creating-a-tenant
TENANT_SUBFOLDER_PREFIX
needs to be added to the settings file. This is the url prefix for the tenant this can’t be left blank.
TENANT_SUBFOLDER_PREFIX = "clients"
In the example given above, the prefixed path ``/r`` will become ``/clients``.
e.g. http://www.mydomain.local/clients/schemaname/ instead of http://www.mydomain.local/r/schemaname/
The middleware is different to the standard middleware. The middleware required is
MIDDLEWARE = (
'django_tenants.middleware.TenantSubfolderMiddleware',
#...
)
Tip
There is an example project for this in the examples folder
Optional Settings¶
-
PUBLIC_SCHEMA_NAME
¶ - Default
'public'
The schema name that will be treated as
public
, that is, where theSHARED_APPS
will be created.
-
TENANT_CREATION_FAKES_MIGRATIONS
¶ - Default
'False'
Sets if the schemas will be copied from an existing “template” schema instead of running migrations. Useful in the cases where migrations can not be faked and need to be ran individually, or when running migrations takes a long time. Be aware that setting this to True may significantly slow down the process of creating tenants.
When using this option, you must also specify which schema to use as template, under
TENANT_BASE_SCHEMA
.
-
TENANT_BASE_SCHEMA
¶ - Default
None
The name of the schema to use as a template for creating new tenants. Only used when
TENANT_CREATION_FAKES_MIGRATIONS
is enabled.
-
TENANT_SYNC_ROUTER
¶ - Default
django_tenants.routers.TenantSyncRouter
The name of the database router that
ready()
checks for when the django_tenant app checks for. If set then place this inDATABASE_ROUTERS
.
DATABASE_ROUTERS = [
# ..
TENANT_SYNC_ROUTER
# ..
]
-
TENANT_MIGRATION_ORDER
¶ - Default
None
A list of fields to order the tenant queryset by when migrating schemas.
Tenant View-Routing¶
-
PUBLIC_SCHEMA_URLCONF
¶ - Default
None
We have a goodie called
PUBLIC_SCHEMA_URLCONF
. Suppose you have your main website atexample.com
and a customer atcustomer.example.com
. You probably want your user to be routed to different views when someone requestshttp://example.com/
andhttp://customer.example.com/
. Because django only uses the string after the host name, this would be impossible, both would call the view at/
. This is wherePUBLIC_SCHEMA_URLCONF
comes in handy. If set, when thepublic
schema is being requested, the value of this variable will be used instead of ROOT_URLCONF. So for example, if you havePUBLIC_SCHEMA_URLCONF = 'myproject.urls_public'
When requesting the view
/login/
from the public tenant (your main website), it will search for this path onPUBLIC_SCHEMA_URLCONF
instead ofROOT_URLCONF
.
Separate projects for the main website and tenants (optional)¶
In some cases using the PUBLIC_SCHEMA_URLCONF
can be difficult. For example, Django CMS takes some control over the default Django URL routing by using middlewares that do not play well with the tenants. Another example would be when some apps on the main website need different settings than the tenants website. In these cases it is much simpler if you just run the main website example.com as a separate application.
If your projects are ran using a WSGI configuration, this can be done by creating a file called wsgi_main_website.py
in the same folder as wsgi.py
.
# wsgi_main_website.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings_public")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
If you put this in the same Django project, you can make a new settings_public.py
which points to a different urls_public.py
. This has the advantage that you can use the same apps that you use for your tenant websites.
Or you can create a completely separate project for the main website.
Caching¶
To enable tenant aware caching you can set the KEY_FUNCTION setting to use the provided make_key helper function which adds the tenants schema_name as the first key prefix.
CACHES = {
"default": {
...
'KEY_FUNCTION': 'django_tenants.cache.make_key',
'REVERSE_KEY_FUNCTION': 'django_tenants.cache.reverse_key',
},
}
The REVERSE_KEY_FUNCTION setting is only required if you are using the django-redis cache backend.
Configuring your Apache Server (optional)¶
Here’s how you can configure your Apache server to route all subdomains to your django project so you don’t have to setup any subdomains manually.
<VirtualHost 127.0.0.1:8080>
ServerName mywebsite.com
ServerAlias *.mywebsite.com mywebsite.com
WSGIScriptAlias / "/path/to/django/scripts/mywebsite.wsgi"
</VirtualHost>
Django’s Deployment with Apache and mod_wsgi might interest you too.