Package: django-tenants
This application enablesdjangopowered websites to have multiple
tenants viaPostgreSQL schemas. A vital feature for every
Software-as-a-Service (SaaS) website.Read the full documentation here:django-tenants.readthedocs.orgDjango provides currently no simple way to support multiple tenants
using the same project instance, even when only the data is different.
Because we don’t want you running many copies of your project, you’ll be
able to have:Multiple customers running on the same instanceShared and Tenant-Specific dataTenant View-RoutingWhat are schemasA schema can be seen as a directory in an operating system, each
directory (schema) with its own set of files (tables and objects). This
allows the same table name and objects to be used in different schemas
without conflict. For an accurate description on schemas, seePostgreSQL’s official documentation on schemas.Why schemasThere are typically three solutions for solving the multitenancy
problem.Isolated Approach: Separate Databases. Each tenant has its own
database.Semi Isolated Approach: Shared Database, Separate Schemas. One
database for all tenants, but one schema per tenant.Shared Approach: Shared Database, Shared Schema. All tenants share
the same database and schema. There is a main tenant-table, where all
other tables have a foreign key pointing to.This application implements the second approach, which in our opinion,
represents the ideal compromise between simplicity and performance.Simplicity: barely make any changes to your current code to support
multitenancy. Plus, you only manage one database.Performance: make use of shared connections, buffers and memory.Each solution has its up and down sides. For a more in-depth
discussion, see Microsoft’s excellent article onMulti-Tenant Data
Architecture.How it worksTenants are identified via their host name (i.e tenant.domain.com). This
information is stored on a table on thepublicschema. Whenever a
request is made, the host name is used to match a tenant in the
database. If there’s a match, the search path is updated to use this
tenant’s schema. So from now on all queries will take place at the
tenant’s schema. For example, suppose you have a tenantcustomerathttp://customer.example.com. Any request incoming atcustomer.example.comwill automatically usecustomer’s schema
and make the tenant available at the request. If no tenant is found, a
404 error is raised. This also means you should have a tenant for your
main domain, typically using thepublicschema. For more information
please read thesetupsection.What can this app do?As many tenants as you wantEach tenant has its data on a specific schema. Use a single project
instance to serve as many as you want.Tenant-specific and shared appsTenant-specific apps do not share their data between tenants, but you
can also have shared apps where the information is always available and
shared between all.Tenant View-RoutingYou can have different views forhttp://customer.example.com/andhttp://example.com/, even though Django only uses the string after
the host name to identify which view to serve.MagicEveryone loves magic! You’ll be able to have all this barely having to
change your code!Setup & DocumentationThis is just a short setup guide. It isstronglyrecommended
that you read the complete version atdjango-tenants.readthedocs.org.YourDATABASE_ENGINEsetting needs to be changed toDATABASES={'default':{'ENGINE':'django_tenants.postgresql_backend',# ..}}Add the middlewaredjango_tenants.middleware.main.TenantMainMiddlewareto the
top ofMIDDLEWARE, so that each request can be set to use
the correct schema.MIDDLEWARE=('django_tenants.middleware.main.TenantMainMiddleware',#...)Adddjango_tenants.routers.TenantSyncRouterto yourDATABASE_ROUTERSsetting, so that the correct apps can be synced depending on what’s
being synced (shared or tenant).DATABASE_ROUTERS=('django_tenants.routers.TenantSyncRouter',)Adddjango_tenantsto yourINSTALLED_APPS.Create your tenant modelfromdjango.dbimportmodelsfromdjango_tenants.modelsimportTenantMixin,DomainMixinclassClient(TenantMixin):name=models.CharField(max_length=100)paid_until=models.DateField()on_trial=models.BooleanField()created_on=models.DateField(auto_now_add=True)classDomain(DomainMixin):passDefine onsettings.pywhich model is your tenant model. Assuming you
createdClientinside an app namedcustomers, yourTENANT_MODELshould look like this:TENANT_MODEL="customers.Client"# app.ModelTENANT_DOMAIN_MODEL="customers.Domain"# app.ModelNow runmigrate_schemas. This will sync your apps to thepublicschema.pythonmanage.pymigrate_schemas--sharedCreate your tenants just like a normal django model. Callingsavewill automatically create and sync the schema.fromcustomers.modelsimportClient,Domain# create your public tenanttenant=Client(schema_name='tenant1',name='My First Tenant',paid_until='2014-12-05',on_trial=True)tenant.save()# Add one or more domains for the tenantdomain=Domain()domain.domain='tenant.my-domain.com'domain.tenant=tenantdomain.is_primary=Truedomain.save()Any request made totenant.my-domain.comwill now automatically set
your PostgreSQL’ssearch_pathtotenant1andpublic, making
shared apps available too. This means that any call to the methodsfilter,get,save,deleteor any other function
involving a database connection will now be done at the tenant’s schema,
so you shouldn’t need to change anything at your views.You’re all set, but we have left key details outside of this short
tutorial, such as creating the public tenant and configuring shared and
tenant specific apps. Complete instructions can be found atdjango-tenants.readthedocs.org.Running the example projectdjango-tenants comes with an example project please seeexamples.CreditsI would like to thank two of the original authors of this project.Bernardo Pires under the namedjango-tenant-schemas.Vlada Macek under the name ofdjango-schemata.RequirementsDjango 2 if you want to use Django 1.11 or lower please use version 1 of django-tenantsPostgreSQLTestingIf you want to run tests, you can either runrun_tests.sh(which requires access to
a PostgreSQL instance, location of which you can customize using theDATABASE_HOSTenv variable) or usedocker-composelike this:## Start Docker service
# start docker   # with Upstart
# systemctl start docker  # with systemd## Install docker-compose (you might want to do this in Python virtualenv)
# pip install docker-compose## In main directory of this repo do:docker-composerun--rmdjango-tenants-test# runs django-tenants tests.
# dockerized PostgreSQL service is started implicitly(note that upon first run theDockerfilewill be built).Video TutorialAn online video tutorial is available onyoutube.DonationIf this project helped you reduce development time, you can give me cake :)