Django中objects.filter未更新字段而objects.get已更新

4 投票
3 回答
6160 浏览
提问于 2025-04-18 02:09

我有一个Django模型:

class QuestionAnswer(models.Model):
   question = models.ForeignKey(Question)
   marks = models.FloatField(null=True)
   graded = models.IntegerField()

现在在命令行中,我执行:

>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa[0].graded 
0
>>> qa[0].graded = 1
>>> qa[0].save()
>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa.graded
0

但是,graded这个字段没有更新。

可是当我执行:

>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded 
0
>>> qa.graded = 1
>>> qa.save()
>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded
1

为什么objects.filter没有更新这个字段,而objects.get却可以呢?

3 个回答

2

filter 会返回一个 QuerySet(看起来像个列表)。在 Django 中,查询集是懒加载的,这意味着它们只有在需要的时候才会被计算出来。

get 则会直接返回实际的对象,这个过程不是“懒”的;所以你看到的变化会立刻生效。

要解决你的问题,你需要对查询集进行计算。你可以通过循环遍历它(用 for 循环),或者把它转换成一个列表来实现。

不过,因为你是通过主键来过滤数据,所以其实你应该使用 get;但要记住,如果找不到这个主键,或者返回了多个对象,get 会抛出异常:

try:
   foo = Foo.objects.get(pk=-1)
except Foo.DoesNotExist:
   print('There is no foo with pk 1')
except Foo.MultipleObjectsReturned:
   print('Oh no! Multiple foos with key 1!')

print('I am the foo with key 1: {}'.format(foo))
3

当你在ORM查询中使用filter来更新数据时,可以试试这个方法。

QuestionAnswer.objects.filter(pk=12345).update(graded=1)
10

试试这个:

>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qa = qs[0]
>>> qa.graded
0
>>> qa.graded = 1
>>> qa.save()
>>> qa = QuestionAnswer.objects.filter(pk=12345)
>>> qa[0].graded
This should be 1

使用 qa[0] 时,其实你并没有在修改和保存同一个对象(虽然它们代表的是相同的SQL数据)。

这是因为查询集是懒惰的:只有在你真正想用查询集返回的数据时,它才会执行SQL查询。查询集的切片工作方式是,查询会被执行,但结果不会被缓存。这意味着每次你使用 qa[0] 时,都会执行一个新的查询,而这些数据会保存在一个新创建的模型实例中。你实际上是在做这个:

>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qa1 = qs.get()
>>> qa1.graded
0
>>> qa2 = qs.get()
>>> qa2.graded = 1
>>> qa3 = qs.get()
>>> qa3.save()

很明显, qa1qa2qa3 是你模型的不同实例:虽然它们有相同的属性值(因为它们代表的是相同的数据库数据),但它们实际上保存在内存中的不同地方,彼此是完全独立的。改变 qa2graded 属性不会对 qa3 产生任何影响,因此当 qa3 被保存时,qa2 的更改不会反映在数据库的更改中。

不过,如果你在切片之前先评估整个查询集,所有结果都会被缓存,那么接下来的操作 就会 生效:

>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qs[0] is qs[0]
False # These are not the same objects in memory...
>>> bool(qs) # This forces evaluation of the entire queryset
True
>>> qs[0] is qs[0]
True # ... but these are!
>>> qs[0].graded
0
>>> qs[0].graded = 1
>>> qs[0].save()
>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qs[0].graded
1

撰写回答