Django: django.utils.functional.SimpleLazyObject的目的是什么?

54

我遇到了一个问题,我将request.user赋值给一个变量prior_user,然后进行身份验证,然后检查是否request.user != prior_user。我预期他们不同,并且prior_user应该包含`AnonymousUser`。令我惊讶的是,他们是相同的。

示例代码:

prior_user = request.user   # request object, obtained froma  view
authenticate_user(request)   # some function that authenticates
print prior_user.username != request.user.username   # returns False i.e.they are the same!

我发现prior_user实际上包含一个django.utils.functional.SimpleLazyObject的实例,因此我认为它是某种延迟查找类型的东西,即prior_user的值直到被实际使用才会被查找。查看源代码后,我无法确认这一点。

有django经验的人能告诉我发生了什么以及为什么需要这样做吗?

这让我感到有些不安,因为通常的赋值语句并不能按照我期望的方式工作,还有哪些Django内部的东西会像这样呢?我也没有在文档中看到相关描述。docs

所以有超级Django知识的人能提供一些澄清吗?

1个回答

119
auth中间件会为request对象添加一个user属性,该属性是SimpleLazyObject类的一个实例。 SimpleLazyObject本身是LazyObject的子类。 从实际代码描述来看,LazyObject是另一个类的包装器,可用于延迟包装类的实例化。

SimpleLazyObject只是通过传入的方法(在这种情况下是get_user方法)设置了该类(即LazyObject_wrapped属性)。以下是该方法的代码:

def get_user(request):
    if not hasattr(request, '_cached_user'):
        request._cached_user = auth.get_user(request)
    return request._cached_user

这本质上只是一个围绕 auth.get_user 的包装器,它启用了一种缓存机制。因此,这实际上是最终运行的内容:

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user
所以,这里实际上发生的事情就是,在实际使用之前,request.user 是含糊不清的。这很重要,因为它允许根据当前的身份验证状态进行适应。如果在身份验证之前访问其属性,则返回一个AnonymousUser实例,但如果您进行身份验证然后访问它,则返回一个User实例。

我有一个疑问,Django中的lazy object和Python中的lazy-object-proxy(https://pypi.org/project/lazy-object-proxy/)是否工作方式相同? - Prateek Gupta
正如你所说的,SimpleLazyObject是LazyObject的子类,那么它们之间有什么区别呢? - Prateek Gupta
通过子类化,您有机会拦截和更改实例化。如果您不需要这样做,请使用SimpleLazyObject。 - HamzDiou
通过子类化,你有机会拦截和修改实例化过程。如果你不需要这样做,可以使用SimpleLazyObject。 - HamzDiou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接