Django Shared Schema Multi Tenant
Project description
Multi Tenant App for Django
Shared schema multi tenant system for Django.
Setup
pip install accrete
-
Add 'accrete' to INSTALLED_APPS
# settings.py INSTALLED_APPS = [ ... 'accrete', 'django...', ]
-
Add accrete.middleware.TenantMiddleware after the auth middleware
# settings.py MIDDLEWARE = [ ... 'django.contrib.auth.middleware.AuthenticationMiddleware', 'accrete.middleware.TenantMiddleware', ... ]
-
Run migrations
Overview
Tenants and their Members are separated by a ForeignKey to accrete.models.Tenant and authenticated by request parameters and headers. On Models inheriting from accrete.modeles.TenantModel and Forms/ModelForms inheriting from accrete.form.(Model)Form, querysets are filtered automatically. The authenticated tenant and member are stored in a contextvar and can be retrieved and set from anywhere in the code.
Models
accrete.models.TenantModel
Abstract Base Model that links to accrete.models.Tenant.
Sets the objects attribute to accrete.managers.TenantManager.
Adds safety and integrity checks to the save method.
Inherit form this model to enable tenant separation.
# models.py
from accrete.models import TenantModel
class MyModel(TenantModel):
...
accrete.models.Tenant
Model representing a tenant. The active tenant for a request/process is stored in a contexvar and can be retrieved and set by accrete.tenant.get_tenant and accrete.tenant.set_tenant respectively.
accrete.models.Member
This model links the user model to the tenant model, allowing users to
be associated with multiple tenants. The member, like the tenant, is authenticated
by the accrete.middleware.TenantMiddleware and stored in a contextvar.
The active member can be retived by accrete.tenant.get_mamber and set by
accrete.tenant.set_member. Using set_member also sets the appropriate tenant.
accret.models.AccessGroup
Arbitrary authorization groups that can be set on tenant or members.
These groups are checked on views decorated by accrete.decorator.tenant_reuired
or subclassed from accrete.mixins.TenantRequiredMixin.
Manager
accrete.managers.TenantManager
Filters QuerySets by the active Tenant.
Ensures the active tenant is set on the instances on bulk operations.
Middleware
tenant.middleware.TenantMiddleware
This Middleware adds the Tenant(request.tenant) and
Member(request.member) attributes to the request object.
On responses the X-TENANT-ID header and tenant_id url parameter is added with
the active Tenant-ID as the value.
If the user is a member of multiple tenants, the request is parsed for a tenant_id in this order.
- The "tenant_id" URL Parameter in the GET data
- The Header X-TENANT-ID
If no tenant could be assigned the two attributes are set to None.
Additionally, the user is checked for membership of the found tenant. If the
user has is_staff set to True, the tenant is set regardless of membership.
The Middleware must be added to the MIDDLEWARE setting after your authentication Middleware as it needs access to request.user.is_authenticated().
Views
tenant.views.TenantRequiredMixin
Adds tenant and optionally access right checks to the dispatch method.
This Mixin is meant as a substitute to
django.contrib.auth.mixins.LoginRequiredMixin as TenantRequiredMixin inherits
LoginRequiredMixin.
tenant.decorator.tenant_required
Substitute for django.contrib.auth.decorators.login_required
Checks if a tenant is set on the request and redirects to the
TENANT_NOT_SET_URL specified in the settings.
The decorator itself is wrapped by login_required and can pass the arguments redirect_field_name and login_url to login_required.
Forms
accrete.forms.Form
Form class that filters the queryset of every field with a queryset attribute.
accrete.forms.ModelForm
Same behavior as tenant.forms.Form. Additionally, sets the tenant on the instance on save if needed.
Fields
accrete.fields.TranslatedCharField
Extends JsonField to store strings in different languages with the language code as the key.
By default, the language specified in settings.LANGUAGE_CODE is used to store values.
from django.db import models
from django.utils import translation
from accrete.utils.models import translated_db_value
class MyModel(models.Model):
name = TranslatedCharField(...)
# Create instance with the language set in settings.LANGUAGE_CODE
instance = MyModel(name='Name in default language')
instance.save()
# Switch to german
translation.activate('de-de')
instance.name = 'Name auf Deutsch'
instance.save()
# Switch back to english
translation.activate('en-us')
instance.refresh_from_db()
print(instance.name)
>>> 'Name in default language'
# Utility to get the saved json as a dict
print(translated_db_value(instance, 'name'))
>>> {'en-us': 'Name in default language', 'de-de': 'Name auf Deutsch'}
# Create an instance with multiple translations at once
translation.activate('de-de')
instance = MyModel(name={'en-us': 'Name in default language', 'de-de': 'Name auf Deutsch'})
instance.save()
print(instance.name)
>>> 'Name auf Deutsch'
Channels
accrete.channels.TenantMiddleware
Support for django-channels. Sets the tenant and member contextvar.
Must be inside auth middleware.
# asgi.py
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(
TenantMiddleware(
URLRouter([
path('path/to/consumer/', Consumer.as_asgi()),
])
)
)
),
})
Consumer
accrete.consumer.WebsocketTenantConsumer
Checks on connect if a tenant is set.
accrete.consumer.JsonWebsocketTenantConsumer
Checks on connect if a tenant is set.
Settings
-
ACCRETE_TENANT_NOT_SET_URL Redirect to this URL when no tenant could be set for an authenticated user.
-
ACCRETE_GROUP_NOT_SET_URL
Redirect to this URL when a tenant or member tries to access a URL without having the needed access rights.
Tenant Utils
Tenant
accrete.tenant.set_tenant(tenant: accrete.models.Tenant)
Stores the tenant in contextvar.
accrete.tenant.get_tenant
Retrieves the tenant from contextvar.
accrete.tenant.set_member(member: accrete.models.Member)
Stores the member and associated tenant contextvar.
accrete.tenant.set_member(member: accrete.models.Member)
Retrieves the member from contextvar.
accrete.tenant.unscoped()
Context Manager to temporally disable tenant isolation.
accrete.tenant.unscope()
Decorator to disable tenant isolation.
accrete.tenant.per_tenant(include: Q = None, exclude: Q = None)
Decorator to run the decorated function per tenant. Tenant to run on can be filtered by the include or exclude arguments.
Contrib
Additional Apps
| App | Description |
|---|---|
| country | Countries with translatable names |
| log | Global auditlog, runs at post_save signal |
| sequence | Configurable sequences with support for subsequences per year. |
| system_mail | Celery mail queue |
| ui | User Interface based on HTMX, Alpine.js and Bulma |
| user | Custom User model |
| user_registration | User registration using system_mail to send confirmation mails |
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 accrete-0.1.2.tar.gz.
File metadata
- Download URL: accrete-0.1.2.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6cfc5ffdf87397af081d787cb2af3e46f9c17c6713877fce6d9c982b3b6f9591
|
|
| MD5 |
ffdf439702bdc07c6a20319c6b68e9c4
|
|
| BLAKE2b-256 |
304110eaf5759204376098c0aacf0bc62148192b51ee658a55c2a92b629f1d88
|
File details
Details for the file accrete-0.1.2-py3-none-any.whl.
File metadata
- Download URL: accrete-0.1.2-py3-none-any.whl
- Upload date:
- Size: 1.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecc7bedfc9955aae3480a054921df5f5705f68f585ac7c794438849a641e78ea
|
|
| MD5 |
1bee4a238737568a7bb10568ce736c44
|
|
| BLAKE2b-256 |
e2a90aee73a7f9298021382b046c8d6f427d996b6409463c886acc83debd02f2
|