我遇到了关于django Queryset的棘手问题

时间:2011-11-30 10:19:58

标签: python django django-queryset

棘手的代码:

user = User.objects.filter(id=123)
user[0].last_name = 'foo'
user[0].save()  # Cannot be saved.
id(user[0])     # 32131
id(user[0])     # 44232 ( different )

用户无法以这种方式保存。

普通代码:

user = User.objects.filter(id=123)
if user:
  user[0].last_name = 'foo'
  user[0].save()  # Saved successfully.
  id(user[0])     # 32131
  id(user[0])     # 32131 ( same )

那么,问题是什么?

3 个答案:

答案 0 :(得分:5)

在第一个版本中,您的user查询集尚未进行评估。因此,每次编写user[0] ORM都会对DB进行独立查询。在第二个变体中,查询集是evalutaed,其作用类似于普通的Python列表。

如果您只想要一行,请使用get

user = User.objects.get(id=123)

答案 1 :(得分:2)

当您索引到查询集时,django会获取数据(或在其缓存中查找)并为您创建模型实例。正如您在id()中发现的那样,每个调用都会创建一个新实例。因此,虽然您可以在这些qs[0].last_name = 'foo'上设置属性,但后续调用qs[0].save()会创建一个新实例(使用原始last_name)并保存

我猜你的特定问题与django缓存查询结果有关。当您只是索引到qs时,没有任何内容被缓存,但是您的调用if users会导致整个(原始)qs被评估,从而被缓存。所以在这种情况下,每次调用[0]都会检索相同的模型实例

答案 2 :(得分:1)

可以保存,但每次访问用户[0]时,实际上都是从数据库中获取它,因此它保持不变。 实际上,当您对Queryset进行切片时,Django会向您的数据库发出SELECT ... FROM ... OFFSET ... LIMIT ...查询。

Queryset不是列表,因此如果您希望它的行为类似于列表,则需要对其进行评估,为此,请在其上调用list()

user = list(User.objects.filter(id=123))

在第二个示例中,调用if user实际上将评估查询集(将其从数据库中获取到您的python程序中),然后使用Queryset的内部缓存。

或者,您可以使用u = user[0],对其进行编辑然后保存,这将有效。

最后,您实际上应该在此处拨打Queryset.get,而不是filter,因为您使用的是唯一密钥。