棘手的代码:
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 )
那么,问题是什么?
答案 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
,因为您使用的是唯一密钥。