Django中objects.filter未更新字段而objects.get已更新
我有一个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()
很明显, qa1
、qa2
和 qa3
是你模型的不同实例:虽然它们有相同的属性值(因为它们代表的是相同的数据库数据),但它们实际上保存在内存中的不同地方,彼此是完全独立的。改变 qa2
的 graded
属性不会对 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