Python/Django
支援分散式多租戶資料庫,如 Postgres+Citus
。
通過將租戶上下文新增到您的查詢來實現輕鬆橫向擴充套件,使資料庫(例如 Citus
)能夠有效地將查詢路由到正確的資料庫節點。
構建多租戶資料庫的架構包括:為每個租戶建立一個資料庫、為每個租戶建立一個 schema
和讓所有租戶共享同一個表。這個庫基於第三種設計,即讓所有租戶共享同一個表,它假設所有租戶相關的模型/表
都有一個 tenant_id
列來表示租戶。
以下連結更多地討論了何時以及如何為您的多租戶資料庫選擇正確架構的權衡:
關於多租戶的其他有用連結:
- https://www.citusdata.com/blog/2017/03/09/multi-tenant-sharding-tutorial/
- https://www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/
專案原始碼
https://github.com/citusdata/django-multitenant
安裝
pip install --no-cache-dir django_multitenant
支援的 Django 版本/前提條件。
Python | Django |
---|---|
3.X | 2.2 |
3.X | 3.2 |
3.X | 4.0 |
用法
為了使用這個庫,您可以使用 Mixins
或讓您的模型從我們的自定義模型類繼承。
模型變化
- 在要使用庫的任何檔案中匯入它:
from django_multitenant.fields import * from django_multitenant.models import *
- 所有模型都應繼承
TenantModel
類。Ex: class Product(TenantModel):
- 定義一個名為
tenant_id
的靜態變數,並使用該變數指定租戶列。Ex: tenant_id='store_id'
TenantModel
子類的所有外來鍵都應使用TenantForeignKey
代替models.ForeignKey
- 實現上述
2
個步驟的示例模型:class Store(TenantModel): tenant_id = 'id' name = models.CharField(max_length=50) address = models.CharField(max_length=255) email = models.CharField(max_length=50) class Product(TenantModel): store = models.ForeignKey(Store) tenant_id='store_id' name = models.CharField(max_length=255) description = models.TextField() class Meta(object): unique_together = ["id", "store"] class Purchase(TenantModel): store = models.ForeignKey(Store) tenant_id='store_id' product_purchased = TenantForeignKey(Product)
使用 mixins 更改模型
- 在您要使用庫的任何檔案中,只需:
from django_multitenant.mixins import *
- 所有模型都應使用
TenantModelMixin
和 djangomodels.Model
或您的客戶模型類Ex: class Product(TenantModelMixin, models.Model):
- 定義一個名為
tenant_id
的靜態變數,並使用該變數指定租戶列。Ex: tenant_id='store_id'
TenantModel
子類的所有外來鍵都應使用TenantForeignKey
代替models.ForeignKey
- 實現上述 2 個步驟的示例模型:
class ProductManager(TenantManagerMixin, models.Manager): pass class Product(TenantModelMixin, models.Model): store = models.ForeignKey(Store) tenant_id='store_id' name = models.CharField(max_length=255) description = models.TextField() objects = ProductManager() class Meta(object): unique_together = ["id", "store"] class PurchaseManager(TenantManagerMixin, models.Manager): pass class Purchase(TenantModelMixin, models.Model): store = models.ForeignKey(Store) tenant_id='store_id' product_purchased = TenantForeignKey(Product) objects = PurchaseManager()
在 db
層自動化複合外來鍵:
- 使用
TenantForeignKey
在租戶相關模型之間建立外來鍵將自動將tenant_id
新增到引用查詢(例如product.purchases
)和連線查詢(例如product__name
)。如果要確保在db
層建立複合外來鍵(帶有tenant_id
),則應將settings.py
中的資料庫ENGINE
更改為django_multitenant.backends.postgresql
。'default': { 'ENGINE': 'django_multitenant.backends.postgresql', ...... ...... ...... }
在哪裡設定租戶?
-
使用中介軟體編寫身份驗證邏輯,該中介軟體還為每個
session/request
設定/取消設定租戶。 這樣,開發人員不必擔心基於每個檢視設定租戶。只需在身份驗證時設定它,庫將確保其餘部分(將tenant_id
過濾器新增到查詢中)。上面的示例實現如下:from django_multitenant.utils import set_current_tenant class MultitenantMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.user and not request.user.is_anonymous: set_current_tenant(request.user.employee.company) return self.get_response(request)
在您的設定中,您需要更新
MIDDLEWARE
設定以包含您建立的設定。MIDDLEWARE = [ # ... # existing items # ... 'appname.middleware.MultitenantMiddleware' ]
-
在您希望基於租戶範圍的所有檢視中使用
set_current_tenant(t)
api 設定租戶。 這將自動(不指定顯式過濾器)將所有django API
呼叫範圍限定為單個租戶。如果未設定current_tenant
,則使用沒有租戶範圍的預設/原生
API。
支援的 API
Model.objects.*
下的大部分API
。Model.save()
為租戶繼承的模型注入tenant_id
。
s=Store.objects.all()[0]
set_current_tenant(s)
#All the below API calls would add suitable tenant filters.
#Simple get_queryset()
Product.objects.get_queryset()
#Simple join
Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')
#Update
Purchase.objects.filter(id=1).update(id=1)
#Save
p=Product(8,1,'Awesome Shoe','These shoes are awesome')
p.save()
#Simple aggregates
Product.objects.count()
Product.objects.filter(store__name='The Awesome Store').count()
#Subqueries
Product.objects.filter(name='Awesome Shoe');
Purchase.objects.filter(product__in=p);