F() expressions Django中解决数据竞态条件
class F¶
An F() object represents the value of a model field, transformed value of a model field, or annotated column. It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory.
Instead, Django uses the F() object to generate an SQL expression that describes the required operation at the database level.
Let’s try this with an example. Normally, one might do something like this:
# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()
Here, we have pulled the value of reporter.stories_filed from the database into memory and manipulated it using familiar Python operators, and then saved the object back to the database. But instead we could also have done:
from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
Although reporter.stories_filed = F('stories_filed') + 1 looks like a normal Python assignment of value to an instance attribute, in fact it’s an SQL construct describing an operation on the database.
When Django encounters an instance of F(), it overrides the standard Python operators to create an encapsulated SQL expression; in this case, one which instructs the database to increment the database field represented by reporter.stories_filed.
Whatever value is or was on reporter.stories_filed, Python never gets to know about it - it is dealt with entirely by the database. All Python does, through Django’s F() class, is create the SQL syntax to refer to the field and describe the operation.
To access the new value saved this way, the object must be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
As well as being used in operations on single instances as above, F() can be used on QuerySets of object instances, with update(). This reduces the two queries we were using above - the get() and the save() - to just one:
reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_filed=F('stories_filed') + 1)
We can also use update() to increment the field value on multiple objects - which could be very much faster than pulling them all into Python from the database, looping over them, incrementing the field value of each one, and saving each one back to the database:
Reporter.objects.update(stories_filed=F('stories_filed') + 1)
F() therefore can offer performance advantages by:
getting the database, rather than Python, to do work
reducing the number of queries some operations require
Avoiding race conditions using F()¶
Another useful benefit of F() is that having the database - rather than Python - update a field’s value avoids a race condition.
If two Python threads execute the code in the first example above, one thread could retrieve, increment, and save a field’s value after the other has retrieved it from the database. The value that the second thread saves will be based on the original value; the work of the first thread will be lost.
If the database is responsible for updating the field, the process is more robust: it will only ever update the field based on the value of the field in the database when the save() or update() is executed, rather than based on its value when the instance was retrieved.
加入F之后方法:
def vote(request,question_id):
question = get_object_or_404(Question,pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError , Choice.DoesNotExist):
return render(request,'polls/detail.html',{
'question':question,
'error_message':"You didn't select a choice.",
})
else:
selected_choice.votes = F('votes')+1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data, This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results',args=(question.id,)))
整理这个内置F方法给我的感觉类似之前部分Orm提供的乐观锁机制,具体实现方式未知。
关于F的文档:
https://docs.djangoproject.com/en/4.1/ref/models/expressions/#avoiding-race-conditions-using-f
这里看到一篇博客解释得还可以:
https://blog.csdn.net/lyp_CSDN/article/details/103703283
看完这篇博客就会发现,Django也可以说是Python做科学计算强于其它编程语言是有理由的。
包括hardcode自定义函数等,在其它编程语言如Java是比较少见的。
本文来自博客园,作者:ukyo--碳水化合物,转载请注明原文链接:https://www.cnblogs.com/ukzq/p/17028148.html

浙公网安备 33010602011771号