request.user返回一个SimpleLazyObject,我该如何“唤醒”它?

73

我有以下方法:

def _attempt(actor):
    if actor.__class__ != User:
        raise TypeError

从视图中调用:

self.object.attempt(self.request.user)

如您所见,_attempt 方法希望 actor 是类型为 django.contrib.auth.models.User 的对象。然而,该对象似乎是类型为 django.utils.functional.SimpleLazyObject 的对象。为什么会这样?更重要的是,我该如何将 LazyObject(显然是 User 对象的一种包装器)转换为 User 对象呢?

有关 Request.user 的更多信息,请参见:https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.user此文档似乎表明,request.user 应该是一个 User 对象...

======编辑后的内容如下:=====

现在我有了以下方法:

def _attempt(obj, action, actor, msg): 
    actor.is_authenticated() 
    if isinstance(actor, LazyObject): 
        print type(actor) 

我传递了一个用户,但是if条件仍然为真,演员仍然是一个LazyObject。为什么会这样?

7个回答

46
请参考 我在类似问题上的回答
Django延迟加载`request.user`,因此它可以是`User`或`AnonymousUser`,这取决于身份验证状态。只有在访问其属性时,它才会“唤醒”并返回适当的类。不幸的是,`__class__`不算,因为那是一个原始类属性。有时您可能需要知道这实际上是一个`SimpleLazyObject`类型,因此将其代理到`User`或`AnonymousUser`是错误的。
长话短说,您无法按照所述方式进行比较。但是,您到底想要实现什么?例如,如果您要检查它是否为`User`或`AnonymousUser`,可以使用`request.user.is_authenticated()`。
总的来说,不应滥用鸭子类型。参数应始终是特定类型或子类型(如`User`或`UserSubClass`),即使不必如此。否则,代码就会变得混乱和脆弱。

2
感谢您的回答 Chris。事实上,我正在尝试检查它是用户还是匿名用户。现在我有了以下方法:`def _attempt(obj, action, actor, msg): actor.is_authenticated() if isinstance(actor, LazyObject): print type(actor)` 我正在传递一个用户,但是if条件仍然为真,actor仍然是LazyObject。为什么会这样? - Himerzi
我意识到我可以在 actor.isauthenticated() 上使用 try:,然后捕获 AttributeException,这将表明 actor 没有 isauthenticated 方法(因此不是用户)。但我仍然有点困惑为什么我之前评论中的代码片段不起作用。 - Himerzi
如果我正确理解了您的原始回复,那么当访问它的属性时,SimpleLazyObject应该被“唤醒”为一个User对象。因此,我本以为调用.is_authenticated()应该会将对象转换为User。这就是我在第一条评论中列出的if条件为什么是false,所以我感到困惑。 - Himerzi
2
我们一直在使用简单的术语进行讲解,但实际上,request.user始终是一个“SimpleLazyObject”。被实例化的用户(无论是“User”还是“AnonymousUser”)被存储为它的一个属性,它仅将请求代理到自身的大多数属性上的用户对象的属性上(“__class__”是一个值得注意的例外)。 - Chris Pratt
我创建了三个User的子类。我的用户类型是Admin、Employee和Relatives,它们都是User的派生类。在登录时,使用User模型并使用多表继承。如何将request.user转换为其中的一种类型? - Vishesh Mangla
显示剩余4条评论

21

这应该可以做到:

# handle django 1.4 pickling bug
if hasattr(user, '_wrapped') and hasattr(user, '_setup'):
    if user._wrapped.__class__ == object:
        user._setup()
    user = user._wrapped

我必须写这个来将一个用户添加到会话字典中。(SimpleLazyObjects无法序列化!)


11
user= request.user._wrapped if hasattr(request.user,'_wrapped') else request.user

那么您可以使用user代替request.user

这类似于UsAaR33的答案,但一行代码更适合转换对象。


4
它类似,但如果"_wrapped"部分未正确初始化,则不会尝试在用户对象上调用"_setup()"。因此,我认为UsAaR33的答案更可取。 - MichaelJones

4

如果你想为代码编写一个失败的“小”单元测试,可以生成一个包装后的 User 并将其放入请求中。

from django.contrib.auth import get_user_model
from django.test import RequestFactory
from django.utils.functional import SimpleLazyObject

user = get_user_model().objects.create_user(
    username='jacob',
    email='jacob@…',
    password='top_secret',
)

factory = RequestFactory()
request = factory.get('/')
request.user = SimpleLazyObject(lambda: user)

请参阅:


1
这可能对其他人有所帮助,而且更加简洁。
Python方法isinstance(instance, class)返回SimpleLazyObject包含的实例是否属于提供的类。
if isinstance(request.user, User):
    user = request.user

0

这是 Django 中 auth_user 表的模式 -

id
password
last_login
is_superuser
username
last_name
email
is_staff
is_active
date_joined
first_name

现在如果你想要写出id,可以这样写 -

req.user.id

如果你想要用户名,请写成这样 -

req.user.username

0

将您的代码更改为以下内容,应该就没有问题了:

from copy import deepcopy

def _attempt(actor):
    actor = deepcopy(actor)
    if actor.__class__ != User:
        raise TypeError

请在您更改的代码中添加说明,解释它的作用以及如何帮助解决原始问题。 - Cray

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