通过Django ORM管理器返回的索引访问列表对象的问题(objects.all())

时间:2012-11-30 15:22:20

标签: django

我有两个课程,他们之间有一对多的关系。

我有一个测试运行,导致父对象('fm')包含两个子对象,可通过相关管理器('changelog')访问。

以下代码运行,表明'changelog'列表包含两个不同的UserMessageStatusUpdate对象,具有不同的属性:

>>> logging.debug(fm.changelog.all())
DEBUG [<UserMessageStatusUpdate: fred changed message status from New to Received>,
    <UserMessageStatusUpdate: fred changed message status from Received to Read>]

我也可以循环使用它们:

>>> for m in fm.changelog.all():
    ....logging.debug(m)
DEBUG fred changed message status from New to Received
DEBUG fred changed message status from Received to Read

但是,如果我尝试通过索引访问每个对象,我会完全得到其他东西:

>>> logging.debug(fm.changelog.all()[0])
DEBUG fred changed message status from Received to Read
>>> logging.debug(fm.changelog.all()[1])
DEBUG fred changed message status from Received to Read

这到底是怎么回事?

[更新1]

我已将ID添加到模型_str_()方法中,所以现在我可以看到index方法的访问返回相同的记录两次:

>>> logging.debug(fm.changelog.all()[0])
DEBUG id:3 fred changed message status from Received to Read
>>> logging.debug(fm.changelog.all()[1])
DEBUG id:3 fred changed message status from Received to Read

所以现在问题是为什么经理在通过索引访问时返回相同的记录? (这是默认的Manager,没什么特别的。)

[更新2]

使用@stevejalim对此进行调查,如果fm.changelog.all()对象显式为list,则测试将通过:

>>> changelog = list(fm.changelog.all())
>>> logging.debug(changelog[0])
DEBUG id:2 fred changed message status from New to Received
>>> logging.debug(changelog[1])
DEBUG id:3 fred changed message status from Received to Read

查看fm.changelog.all():

>>> logging.debug(type(fm.changelog.all()))
DEBUG <class 'django.db.models.query.QuerySet'>

似乎问题可能与QuerySet返回Generator有关,并且无法通过索引直接访问项目。或者可能与RelatedManager课程中的这一行相关:https://github.com/django/django/blob/stable/1.4.x/django/db/models/fields/related.py#L458

def get_query_set(self):
    try:
        return self.instance._prefetched_objects_cache[rel_field.related_query_name()]

基本上,这是一个谜,但是投入到列表中可以完成工作.1

1 个答案:

答案 0 :(得分:3)

正如评论中所讨论的,解决方法是将QuerySet转换为列表。我强烈怀疑这是底层django实现中的一个错误,因为在通过索引访问时返回错误元素是出乎意料的行为,但我无法确切地知道它出错的地方。

默认行为会产生错误的(意外)结果:

>>> changelog = fm.changelog.all()
>>> changelog[0]
id:3 fred changed message status from Received to Read
>>> changelog[1]
id:3 fred changed message status from Received to Read

转换为列表会得到正确的(预期)结果:

>>> changelog = list(fm.changelog.all())
>>> changelog[0]
id:2 fred changed message status from New to Received
>>> changelog[1]
id:3 fred changed message status from Received to Read