python - 使用 Python 3.7 contextvars 在 Django 视图之间传递状态
问题描述
我正在使用 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
.
谢谢。
解决方案
您只是在生成响应后设置上下文。这意味着它总是会滞后。您可能想在之前设置它,然后检查用户是否已更改。
请注意,尽管我不确定这是否会完全按照您的意愿工作。上下文变量根据定义是本地的;但是在像 Django 这样的环境中,你永远不能保证来自同一个用户的连续请求将由同一个服务器进程提供服务,同样一个进程可以服务来自多个用户的请求。另外,正如您所指出的,Celery 又是一个单独的进程,它不会共享上下文。
推荐阅读
- android - 在视图模型之间共享数据
- laravel-5 - Laravel Bootstrap 错误无法在我的电脑上运行,但在另一台电脑上运行良好
- linux - 无法让 nginx 在我的公共 IP 上服务
- linux - 连接EC2实例的私钥
- c++ - 标头中的 const 和 constexpr 变量是否应该是 inline 以防止 ODR 违规?
- javascript - 带有 ngx-translate 库 angular 4/5 下拉菜单的 html 中的 ng 模型
- php - 在 PHP (AWS/ResulData) 中访问多维关联数组时出错
- javascript - GraphQL 查询返回错误 .filter 不是函数
- javascript - 一个盒子不适合不同的屏幕分辨率
- web-scraping - 网页抓取产品详细信息,而不是 UIPath 中的列表/表格