Django 1.6 Models (VI) — More QuerySet

用着用着发现诸多不顺,其实 Django 提供的基础 orm 还有很多工具之前我是不知道的,这样自然如此。

现在就要把这些 More 有关 QuerySet 的东西抽出来,这样才可以写得出想要的复杂的查询。

1. 使用 pickle 缓存模型对象

pickle 可以用一个文件简单地对对象进行持久化,如果我们使用 pickle 看来缓存 models 对象,就会将当时的模型数据进行缓存,我们从 pickle 调出这个模型的时候,得到的也是当时缓存的时候的数据,这完全可以假设为 dirty 的。

如果仅仅希望缓存这个查询而不希望缓存这个查询的数据,可以在恢复 pickle 之后刷新一下这个 queryset 的查询语句,像下面这样:

import pickle
query = pickle.loads(s)     # Assuming 's' is the pickled string.
qs = MyModel.objects.all()
qs.query = query            # Restore the original 'query'.

2. QuerySet 的属性

a. QuerySet.db:

返回这个 QuerySet 的查询使用的数据库名字(字符串);

注意这个字符串的含义,在 settings.py 里面的 DATABASES 配置块,一般会有一个 ‘default’ 的配置段来制定一个数据库,但其实是可以有多个的,调用 db 属性会返回这个 key;

b. QuerySet.ordered:

返回这个 QuerySet 是否已序,返回 True/False;

3. QuerySet 的方法(返回 QuerySet)

a. filter(**kwargs):

你懂的;

b. exclude(**kwargs):

你懂的;

c. annotate(*args, **kwargs):

前面 aggregation 那里讲过;

d. order_by(*fields):

你懂的;

e. reverse():

你懂的;

f. distinct([*fields]):

之前少用些,不过你懂的;

g. values(*fields):

aggregation 那里也讲过,返回一个 ValuesQuerySet,其中每个元素是一个 dict;

h. values_list(*fields):

好了这个有意思,类似于 values,但是返回的 ValuesListQuerySet 其中每个元素是一个 tuples,而列名是再也找不回来的了;

i. dates(field, kind, order=’ASC’):

这个家伙是一个快捷工具,可以按照 kind (year/month/day) 来汇总日期字段,直接返回一个 date 的 list;

j. datetimes(field, kind, order=’ASC’, tzinfo=None)

类似 dates,不过精确到时间,kind 取值范围是 (year/month/day/hour/minute/second)

k. none():

返回一个空的 QuerySet,挺有用的;

l. all():

你懂的;

m. select_related():

这个比较有用,值得注意;

返回一个 QuerySet,提前缓存其所有外键,如果确认程序下文将使用这个 QuerySet 的 related_objects,就尽管先用 selected_related 先缓存一下;

注意外键的缓存是级联的,但是 null=True 的外键将不被缓存;

另外,这个方法仅支持 ForeignKey 和 OneToOneField 的关系;

n. prefetch_related(*lookups):

这个方法也是缓存,基本用途与 select_related 相同;

与 select_related() 不同的时候,这个方法可以通过 lookups 参数表指定需要混存的字段,而且缓存的对象范围还可以包括 ManyToManyFields 和多对一的关系;

关于上面两个缓存处理的再一点说明,如果使用了这两种方式的缓存,所有的关联数据库对象即被全部读取到内存中,在其上使用成员属性方位关联对象的时候,将调用缓存;但是如果在上面再通过其他方法去处理 QuerySet(例如 filter),缓存将被忽略;

o. extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None):

超级有用啊!不会这个会死的!

问题是这样,这个方法用于在当前 QuerySet 的对象里面附加字段,类比一下:

annotate 是将当前的所有属性聚合之后将聚合的计算结果作为属性附加到当前的 QuerySet 上面,而 extra 方法则是不产生聚合,通过当前的单行对象计算出来额外的字段附加到 QuerySet 的对象中;

其中的参数可以高度定制化一个查询,具体解析如下:

* select [dict]:

dict 的 key 是输出属性的名称(相当于查询的字段别名),对应的 value 则是这个字段的直接运算方式,会直接送给 sql 语句;

注意:直接,是直接!所以这里可以变态到嵌一个 RAW 的子查询!又犯戒了!小心 sql 注入!

另外注意 select 参数可以是一个 django.utils.datastructures.SortedDict 对象来确保字段的顺序,里面的内容也可以用 %s 来从 params 参数表里面取得参数。

* where [list]:

提供一个 list 的字符串,每个元素是一个 where 条件,它们会被直接用 AND 串接起来注入到 where 子句里面;

注意:还是直接!里面也可以放子查询的,官方文档特别提示了当一个表名出现多次的时候会引发错误,但本身这样的逻辑时很混乱的,不建议用这个把事情搞的太复杂。

* tables [list]:

可以指定更多的 from 子句引用的 tables!碉堡了!简直是混乱!

* order_by[list]:

跟普通的 order_by 格式是一样的,但放在这个地方有一定的方便性!

* params, select_params:

提供一个参数列表,可以供 where 参数和 select 参数里面使用 %s 代为调用,会自动 clean 一下防止注入的;

p. defer(*fields):

在结果集中声明不 select 某些字段(以期提高性能)

q. only(*fields):

在结果集中声明“仅” select 某些字段(以期提高性能)

r. using(alias):

声明用哪个数据库配置来查询,使用的 alias 对应于 settings.py 里面的 DATABASES 配置块里面的 key;

s. select_for_update(nowait=False):

声明一个锁,在 select 之后,事务结束之前不允许 select 出来的行被修改。

比较复杂而且容易引起不同数据库之间的兼容性问题,因此不深究先;

4. QuerySet 的方法(不返回 QuerySet)

a. get(**kwargs):

获取对象,你懂的;

注意如果 get 到没有记录,会抛出 DoesNotExist 异常;

如果 get 到多个记录,会抛出 MultipleObjectsReturned 异常;

b. create(**kwargs):

你懂的;

注意如果参数里面指定了主键而发生了冲撞的话,会抛出 IntegrityError 异常;

c. get_or_create(**kwargs):

获取一个对象,如果没有匹配到就创建它;

返回一个 tuples: (object, created),object 是获取或者创建出来的模型对象;created 是一个布尔值,来标示到底这个对象是创建出来的还是获取出来的;

在参数表里面,除了 defaults 以外,其他的参数都会被用来交给一个 get 方法调用;

如果 get 到多个记录,会抛出 MultipleObjectsReturned 异常;

如果没有 get 到记录,则会根据 get 的参数合并 defaults 然后创建一个记录;

d. bulk_create(objs, batch_size=None):

批量创建一堆对象,注意:

  • 单个对象的 save 方法不会被调用,并且 pre_save 和 post_save 钩子不会被触发;

  • auto field 不会生效

e. count():

你懂的

f. in_bulk(id_list):

怪怪的,id_list 提供一个主键的范围,这个方法会返回一个 dict,key 是实际匹配到的主键(必须在 id_list 里面,但匹配不到的话会被忽略),value 就是实际的对象;

g. iterator():

获取一个 QuerySet 的迭代器,可以单步逐个调,其实在关注性能的情况下是非常有用的;

h. earliest/latest(field_name=None):

返回指定字段日期最早(晚)的那个对象;

i. first/last():

你懂的;

j. aggregate(*args, **kwargs):

前面讲过了,你懂的;

k. exists():

返回 bool 你懂的;

l. update(**kwargs)/delete():

你懂的

5. 字段查找方法

这些方法主要用于 __ 双下划线的间接调用,一般在 kwargs 里面使用。

a. exact:

字段精确匹配,如果数据库设置支持,使用这个方法会强制大小写敏感;

b. iexact:

字段精确匹配但说明不区分大小写,被翻译成 ILIKE ‘…’;

c. contains/icontains:

字符串 like ‘%…%’,有没有 i 表示是否区分大小写;

d. in:

提供一个集合,指定的字段在集合中

e. gt/gte/lt/lte:

大于/大于等于/小于/小于等于

f. startswith/istartswith/endswith/iendswith:

你懂的

g. range

提供一个二元 tuple,被翻译成 between;

h. year/month/day/weekday/hour/minute/second:

提取日期时间字段的 datepart 部分;

i. isnull

提供一个 bool,判断是否为空;

j. search

全文搜索,具体语法参见文档好了,之前没有太研究过;

k. regex/iregex

区分大小写和不区分大小写的正则匹配

6. 聚合函数

a. class Sum(field) / Avg(field) / Max(field) / Min(field)

你懂的

b. class Count(field, distinct=False)

你懂的,注意 distinct,如果是 True,则完全相同的记录会仅被计算一次

c. class StdDev(field, sample=False)

返回指定字段的标准差?!高大上啊,返回一个 float;如果 sample 为真,则返回采样标准差(回去恶补概率统计啊)。

d. class Variance(field, sample=False)

返回方差,基本同上。

现就这样,好多啊,感觉最有用的还是 extra 函数。但是用太多这类工具就会引入不少与数据库层的耦合,不用又完成不了太多工作,但总比没有强太多了。


【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2014/05/django-models-6.html【Django 1.6 Models (VI) — More QuerySet】

发表评论

电子邮件地址不会被公开。 必填项已用*标注