GavinGuan 2017-04-18
class F
一个 F()对象代表了一个model的字段值或注释列。使用它就可以直接参考model的field和执行数据库操作而不用再把它们查询出来放到python内存中。作为代替,Django使用 F()对象生成一个SQL表达式,来描述数据库层级所需要的操作,这些通过一个例子可以很容易的理解。往常,在数据库中获取一个数据会这样做:
(终端实验)
>>>q = OB.objects.filter(name='Tintin') >>>q[0].number += 1 (值不变)
或者:
q = OB.objects.get(name='Tintin') q.number += 1(值增加,但是并未保存进数据库,加上q.save()即可)
这里,我们把 q 的 number 属性值从数据库取出放到内存中并用我们熟悉的python运算符操作它,最后再把它保存到数据库。然而,还可以这样做:
from django.db.models import F q = OB.objects.get(name='Tintin') q.number = F('number') + 1
虽然 q[0].number = F('number') + 1 看起来像一个正常的Python分配值赋给一个实例属性,事实上这是一个描述数据库操作的SQL概念。
>>>q.number <CombinedExpression(联合表达): F(number) + Value(1)> 下面有具体例子
当Django遇到 F()实例,它覆盖了标准的Python运算符创建一个封装的SQL表达式。在这个例子中,q[0].number就代表了一个指示数据库对该字段进行增量的命令。
无论q[0].number的值是或曾是什么,Python完全不知道,这完全是由数据库去处理的。所有的Python,通过Django的F() 类,只是去创建SQL语法参考字段和描述操作。
为了获得用这种方法保存的新值,此对象应重新加载:
q = OB.objects.get(name='Tintin')
和上面单独实例的操作一样, F()配合 update()可以应用于对象实例的 QuerySets这减少了我们上面使用的两个查询 get() 和 save() 只需要使用如下方式:
>>> q = UserItem.objects.filter(name='雪碧') >>> q.update(number=F('number')+1) 1 >>> q[0].number Decimal('6')
我们可以使用update()方法批量地增加多个对象的字段值。这比先从数据库查询后,通过循环一个个增加,并一个个保存要快的很多。
UserItem.objects.all().update(number=F('number')+1)
F()表达式的效率上的优点主要体现在
1.直接通过数据库操作而不是Python
2.减少数据库查询次数
使用 F() 的另一个好处是通过数据库而不是Python来更新字段值以避免竞态条件。
如果两个Python线程执行上面第一个例子中的代码,一个线程可能在另一个线程刚从数据库中获取完字段值后获取、增加、保存该字段值。第二个线程保存的值将会基于原始字段值;第一个线程的工作将会丢失。如果让数据库对更新字段负责,这个过程将变得更稳健:它将只会在 save() 或 update()执行时根据数据库中该字段值来更新字段,而不是基于实例之前获取的字段值。同时F()在查询集和过滤器中也十分有用,它使得使用条件通过字段值而不是Python值过滤一组对象变得可能,例如:
q = OB.objects.filter(number=F('pro_number'))
直接获取了实际数量大于优惠数量的商品信息,不需要获取两个数量之后比较再存入新数组,同时搭配查询集的选择器能进行更多方便的操作。
F()可用于通过将不同字段与算术相结合来在模型上创建动态字段:
q = OB.objects.annotate(new_number=F('number') - F('pro_number')){annotate方法添加注释}
如果你组合的字段是不同类型,你需要告诉Django将返回什么类型的字段。由于F()不直接支持output_field,您需要使用Expression Wrapper
{Expression Wrapper简单地包围另一个表达式,并提供对其他表达式可能不可用的属性(例如output_field)的访问。当对 F() 的 annotations 中描述的不同类型的F()表达式使用算术时,必须使用 Expression Wrapper, class ExpressionWrapper(expression, output_field) }
from django.db.models import DateTimeField, ExpressionWrapper, F OB.objects.annotate( expires=ExpressionWrapper( F('active_a') + F('active_b'), output_field=DateTimeField()))