gpt4 book ai didi

python - 使用 Python 3.7 contextvars 在 Django View 之间传递状态

转载 作者:行者123 更新时间:2023-12-01 07:27:03 30 4
gpt4 key购买 nike

我正在使用 Django 2.2 和 Python 3.7 构建单个数据库/共享架构 Multi-Tenancy 应用程序。

我正在尝试使用新的 contextvars API 在 View 之间共享租户状态(组织)。

我在自定义中间件中设置状态,如下所示:

# 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...

我遇到的问题是它并不总是有效 - 特别是在这些情况下:

  1. 在调用 login() 后,在我的登录 View 中,中间件运行,我可以看到租户设置正确。然而,当我从登录 View 重定向到主视图时,状态(最初)再次为空,并且在主视图执行后似乎已正确设置。如果我重新加载主视图,一切都会正常。

  2. 如果我注销然后以其他用户身份再次登录,则前一个用户的状态将被保留,直到重新加载页面为止。这似乎与上一期有关,因为它几乎看起来像是国家滞后了(因为缺乏更好的词)。

  3. 我使用 Celery 来分离 shared_tasks 进行处理。我必须手动将租户传递给这些,因为它们不会获取上下文。

问题:

  1. 我这样做正确吗?

  2. 我需要在每个模块中以某种方式手动重新加载状态吗?

很沮丧,因为我几乎找不到这样做的例子,而且对 contextvars 的讨论也很少。我试图避免在任何地方手动传递租户或使用 thread.locals

谢谢。

最佳答案

您只需在生成响应后设置上下文。这意味着它永远会滞后。您可能想先设置它,然后检查用户是否已更改。

请注意,我不太确定这是否会完全按照您想要的方式工作。根据定义,上下文变量是本地的;但在像 Django 这样的环境中,你永远无法保证来自同一用户的连续请求将由同一个服务器进程提供服务,同样,一个进程可以服务来自多个用户的请求。另外,正如您所指出的,Celery 又是另一个单独的进程,它不会共享上下文。

关于python - 使用 Python 3.7 contextvars 在 Django View 之间传递状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57397754/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com