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
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django-easy-tenants-0.3.0.tar.gz.
File metadata
- Download URL: django-easy-tenants-0.3.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.9 CPython/3.8.2 Linux/5.4.0-39-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aaa4c544fad2ed35009e8b570e6da2164a1d3412ef8a2d56ec6e07554df11dac
|
|
| MD5 |
b3dc51db7bb83afd0f99a58d9cfb0188
|
|
| BLAKE2b-256 |
ccd1228acb251a70cd4ba9196af0e83a086554d268e4d171ed5f71500e3e405e
|
File details
Details for the file django_easy_tenants-0.3.0-py3-none-any.whl.
File metadata
- Download URL: django_easy_tenants-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.9 CPython/3.8.2 Linux/5.4.0-39-generic
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
522d00b8089b3ef6dbef539a6be851f3bb8a04804a72d92c4be0b4014ab16fd6
|
|
| MD5 |
2ef8098e943de5e39f31ec4590354706
|
|
| BLAKE2b-256 |
dfa6b0dff9c0c52cd1bfb8817cbacb8a56c52a66aa43db74cde9a88b07ceddfd
|