我正在使用 Django 2.2 和 Python 3.7 构建单个数据库/共享模式多租户应用程序。
我正在尝试使用新的contextvars
apiOrganization
在视图之间共享租户状态 (an)。
我在这样的自定义中间件中设置状态:
# tenant_middleware.py
from organization.models import Organization
import contextvars
import tenant.models as tenant_model
tenant = contextvars.ContextVar('tenant', default=None)
class TenantMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
user = request.user
if user.is_authenticated:
organization = Organization.objects.get(organizationuser__is_current_organization=True, organizationuser__user=user)
tenant_object = tenant_model.Tenant.objects.get(organization=organization)
tenant.set(tenant_object)
return response
我通过让我的应用程序的模型从TenantAwareModel
这样的继承来使用这种状态:
# tenant_models.py
from django.contrib.auth import get_user_model
from django.db import models
from django.db.models.signals import pre_save
from django.dispatch import receiver
from organization.models import Organization
from tenant_middleware import tenant
User = get_user_model()
class TenantManager(models.Manager):
def get_queryset(self, *args, **kwargs):
tenant_object = tenant.get()
if tenant_object:
return super(TenantManager, self).get_queryset(*args, **kwargs).filter(tenant=tenant_object)
else:
return None
@receiver(pre_save)
def pre_save_callback(sender, instance, **kwargs):
tenant_object = tenant.get()
instance.tenant = tenant_object
class Tenant(models.Model):
organization = models.ForeignKey(Organization, null=False, on_delete=models.CASCADE)
def __str__(self):
return self.organization.name
class TenantAwareModel(models.Model):
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='%(app_label)s_%(class)s_related', related_query_name='%(app_label)s_%(class)ss')
objects = models.Manager()
tenant_objects = TenantManager()
class Meta:
abstract = True
.tenant_objects...
在我的应用程序中,业务逻辑可以使用模型类而不是检索查询集.objects...
我遇到的问题是它并不总是有效 - 特别是在这些情况下:
在
login()
调用后的登录视图中,中间件运行,我可以看到租户设置正确。但是,当我从登录视图重定向到主视图时,状态(最初)再次为空,并且似乎在主视图执行后正确设置。如果我重新加载主视图,一切正常。如果我注销然后以其他用户身份再次登录,则会保留前一个用户的状态,直到重新加载页面。这似乎与上一期有关,因为状态似乎落后了(因为没有更好的词)。
我使用 Celery
shared_tasks
进行加工。我必须手动将租户传递给这些,因为它们不会获取上下文。
问题:
我这样做正确吗?
我是否需要在每个模块中以某种方式手动重新加载状态?
沮丧,因为我几乎找不到这样做的例子,也很少讨论contextvars
. 我试图避免到处手动传递租户或使用thread.locals
.
谢谢。