Django - 解决生产中 DRF ViewSets 的线程局部变量和中间件问题

Django - Troubleshooting thread locals & middleware issue with DRF ViewSets in Production

有人对解决这个具有挑战性的问题有建议吗?不确定如何进一步解决此问题。

我正在使用以下方法在中间件的线程局部变量中设置一个值:

from threading import local

ORG_ATTR_NAME = getattr(settings, "LOCAL_ORG_ATTR_NAME", "_current_org")

_thread_locals = local()

def _do_set_current_variable(variable_fun, attr_name):
    setattr(_thread_locals, attr_name, variable_fun.__get__(variable_fun, local))

def thread_local_middleware(get_response):

    def middleware(request):
        organization = Organization.objects.get_current(request)
        _do_set_current_variable(lambda self: organization, ORG_ATTR_NAME)

        response = get_response(request)

        return response

    return middleware

def get_current_variable(attr_name: str):
    """Given an attr_name, returns the object or callable if it exists in the local thread"""
    current_variable = getattr(_thread_locals, attr_name, None)
    if callable(current_variable):
        return current_variable()
    return current_variable

我有一个管理器 class,所有包含 organization FK 字段的模型都使用:

class OrganizationAwareManager(models.Manager):
    def get_queryset(self):
        logger.debug(f"_thread_locals attributes in manager: {_thread_locals.__dict__}")
        organization = get_current_variable(ORG_ATTR_NAME)
        queryset = super().get_queryset().filter(organization=organization)
        logger.debug(f"OrganizationAwareManager queryset: {queryset.query}")
        return queryset

这一切在开发中都非常有效,并且在生产中的几乎所有方面都非常有效 - 除了在使用 OrganizationAwareManager 查询模型的 DRF ViewSets 中。其他 ViewSet 按预期工作,并且在视图上下文中引用具有 OrganizationAwareManager 的模型的普通 django 视图也可以正常工作。从字面上看,生产中 OrganizationAwareManager 的 DRF 视图集是唯一的问题。

如您所见,我在管理器中添加了日志记录以检查 _thread_locals 上设置了哪些属性。

在开发中我得到类似的东西:_thread_locals attributes in manager: {'_current_org': <bound method thread_local_middleware.<locals>.middleware.<locals>.<lambda> of <function thread_local_middleware.<locals>.middleware.<locals>.<lambda> at 0x7f1820264a60>>}

在产品中,似乎没有任何设置:_thread_locals attributes in manager: {}

关于 DRF 如何处理请求,我可能遗漏了什么吗?它应该是 运行 通过中间件堆栈并设置我的属性,无论我们是在开发还是生产,对吧?我似乎找不到可能解释这一点的两种环境之间的任何差异。两者非常相似,使用 docker-compose 和几乎相同的容器。我在 prod 环境中尝试了 gunicorn 和 gunicorn+uvicorn,症状没有区别。

我已经尝试解决这个问题 3 天了,但 运行 没有想法,所以解决这个问题的建议 非常 不胜感激。

真奇怪。一段时间后,我有了解决问题的想法。

仍然不完全确定根本原因(很想知道为什么它在开发中工作得很好,但在进行以下更改之前在生产中却没有),但似乎在 ViewSets 中定义了查询集class 本身,当线程开始时对查询求值?

我在我的日志中注意到,当我启动服务器时,我从 OrganizationAwareManager 得到了一大堆日志条目,说 _thread_locals 没有关联的属性。这些日志条目的数量似乎与我项目中使用 OrganizationAwareManager 的 ViewSet 的数量大致相同。他们都在系统启动时进行了初始评估,orgnization=None,因此将丢弃对 organization 的任何进一步过滤。

像下面这样的 ViewSets 最终没有被 organization 正确过滤:

class AssetTypeViewSet(viewsets.ModelViewSet):
    queryset = AssetType.objects.all()
    serializer_class = AssetTypeSerializer

当我修改以在 get_queryset() 中定义查询集以便在执行 ViewSet 时对其进行评估,现在一切正常:

class AssetTypeViewSet(viewsets.ModelViewSet):
    queryset = AssetType.objects.none()
    serializer_class = AssetTypeSerializer

    def get_queryset(self):
        return AssetType.objects.all()

奇怪。