Thursday, 15 October 2015

Why does objects.filter not updating the field but objects.get works?

Problem
I have a Django model :
class QuestionAnswer(models.Model):
   question = models.ForeignKey(Question)
   marks = models.FloatField(null=True)
   graded = models.IntegerField()
Now in command line I do:

>>> 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
The graded field is not updated.
But when I do:
>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded 
0
>>> qa.graded = 1
>>> qa.save()
>>> qa = QuestionAnswer.objects.get(pk=12345)
>>> qa.graded
1
Why does objects.filter not updating the field but objects.get works.

Solution: 
try this solution

>>> 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
By using qa[0], you're not actually modifying and saving the same object (even though they represent the same SQL data).
This is due to the fact that querysets are lazy: they only execute the sql query when you actually try to use the data that the queryset would return. The way slices work with querysets, the query is executed but the result will not be cached. This means that whenever you use qa[0], a new query is executed, and that data is saved in a newly created model instance. What you are effectively doing is this:
>>> qs = QuestionAnswer.objects.filter(pk=12345)
>>> qa1 = qs.get()
>>> qa1.graded
0
>>> qa2 = qs.get()
>>> qa2.graded = 1
>>> qa3 = qs.get()
>>> qa3.save()
It should be obvious that qa1qa2 and qa3 are different instances of your model: while they have the same attribute values (as the represent the same database data), they are actually saved in different places in memory and are completely separate from each other. Changing the gradedattribute on qa2 will not in any way affect qa3, so when qa3 is saved, the changes to qa2won't be reflected in the changes in the database.

However, if you were to evaluate the entire queryset before slicing, all results would be cached, and the following would work:


>>> 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


TO see in Detail about Dejnago queries visit django queries in detail

No comments:

Post a Comment