Easy to create applications that use tenants in django
Project description
easy-tenants
This is a Django app for managing multiple tenants on the same project instance using a shared approach.
Background
There are typically three solutions for solving the multitenancy problem:
- Isolated Approach: Separate Databases. Each tenant has it’s 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 third approach, which in our opinion, is the best solution for a large amount of tenants.
For more information: Building Multi Tenant Applications with Django
Below is a demonstration of the features in each approach for an application with 5000 tenants.
Approach | Number of DB | Number of Schemas | Django migration time | Public access |
---|---|---|---|---|
Isolated | 5000 | 5000 | slow (1/DB) | No |
Semi Isolated | 1 | 5000 | slow (1/Schema) | Yes |
Shared | 1 | 1 | fast (1) | Yes |
How it works
The following image shows the flow of how this application works.
Instalation
Assuming you have django installed, the first step is to install django-easy-tenants
.
pip install django-easy-tenants
Now you can import the tenancy module in your Django project.
Setup
It is recommended to install this app at the beginning of a project. In an existing project, depending on the structure of the models, the data migration can be hard.
Add easy_tenants
to your INSTALLED_APPS
on settings.py
.
settings.py
INSTALLED_APPS = [
...,
'easy_tenants',
]
Create a model which will be the tenant of the application.
yourapp/models.py
from easy_tenants.models import TenantMixin
class Customer(TenantMixin):
...
Define on your settings.py
which model is your tenant model. Assuming you created Customer
inside an app named yourapp
, your EASY_TENANTS_MODEL should look like this:
settings.py
EASY_TENANTS_MODEL = 'yourapp.Customer'
Your models, that should have data isolated by tenant, need to inherit from TenantAbstract
and the objects need to be replaced by TenantManager()
.
from django.db import models
from easy_tenants.models import TenantAbstract
from easy_tenants.managers import TenantManager
class Product(TenantAbstract):
name = models.CharField(max_length=10)
objects = TenantManager()
Add the middleware easy_tenants.middleware.DefaultTenantMiddleware
to your middleware classes.
Need to be included after django.contrib.auth.middleware.AuthenticationMiddleware
.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'easy_tenants.middleware.DefaultTenantMiddleware',
]
Include the django-easy-tenants
urls.
path('easy-tenants/', include('easy_tenants.urls')),
You need to create a view that will list all your tenants and then include the name of that view in the settings. This is how the user chooses a tenant that will be saved in the user's session.
views.py
from django.shortcuts import render
def tenant_list(request):
user_tenants = request.user.tenants.all()
return render(request, 'tenant_list.html', {
'object_list': user_tenants
})
tenant_list.html
...
<ul>
{% for object in object_list %}
<li>
<form action="{% url 'easy_tenants:set-current-tenant' object.pk %}" method="post">
{% csrf_token %}
<button type="submit">Use {{ object.name }}</button>
</form>
</li>
{% endfor %}
</ul>
...
urls.py
path('tenants/', tenant_list, name='tenant-list'),
settings.py
EASY_TENANTS_REDIRECT_URL = 'tenant-list'
After choosing the tenant, the user is redirected to a URL defined in the
settings EASY_TENANTS_SUCCESS_URL
.
settings.py
EASY_TENANTS_SUCCESS_URL = 'home'
If a URL is accessed and there is no tenant defined in the session, the user is redirected to
EASY_TENANTS_REDIRECT_URL
. If you want to ignore some views you can add a mixin or decorator to your view,
like below.
(views of django.contrib.auth
that do not require authentication are ignored)
from easy_tenants import TenantNotRequiredMixin, tenant_not_required
class MyView(TenantNotRequiredMixin, View):
...
@tenant_not_required
def my_view(request):
...
If you want to separate the upload files by tenant, you need to change the DEFAULT_FILE_STORAGE
configuration (only available for local files).
DEFAULT_FILE_STORAGE = 'easy_tenants.storage.TenantFileSystemStorage'
Running the example project
python manage.py migrate
python manage.py createsuperuser
python manage.py shell # create 2 customers
python manage.py runserver
Access the page /admin/
, create a Customer
and then add a user on the created Customer
.
Motivation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django-easy-tenants-0.3.2.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 01fcf1d29691b00d13a7bdfc9a1f2395254a0c20473f597bd2e9e87c16135463 |
|
MD5 | d91539432d879b9f92d0a515f0001366 |
|
BLAKE2b-256 | 4335589abaf05253b625a9a933a7461bbee36be61bba153d9980f01d5e81b939 |
Hashes for django_easy_tenants-0.3.2-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1655f1815384f0626cc537208c9abef4deadf4b9da1d81f24c75dfe7763afd22 |
|
MD5 | f7a199fb6692526586bbeaddd6605ff3 |
|
BLAKE2b-256 | 079e4528c6780080e6225db629eda45b4553e490c572dab115c9b840e04508df |