## 较少被用到的查询对象
所谓查询对象,其实就是在queryset中可以用来包裹字段名称字符串的对象,简而言之,就是类似F,Q这样的对象,F和Q本身算是Django中非常基础的查询对象,基本上可以说不用到它们一个django框架就很难搭起来,所以将它们归入了基础目录中,但实际上Django还存在很多这样的查询对象,如模仿SQL聚合函数的Count, Avg, Sum对象,还有辅助多表查询的Prefetch对象等等,这里主要就是写一下它们的用法
****
### Prefetch对象
在昨天之前可以说对这个对象具体的作用一无所知,这里来详细写一下理解
Prefetch和Q,F等对象一样,都是一个在queryset中被用到的查询对象一般的东西,它最简单的用法,就是配合它能用到的queryset函数prefetch_related
假设有如下模型
~~~py
class ObjectFields(CommonInfo):
TEXT_TYPE = (
('text', 'text'),
('email', 'email'),
('url', 'url'),
)
id = models.CharField(primary_key=True, max_length=36)
depending_on = models.ForeignKey('ObjectFields', models.DO_NOTHING,
related_name='dependants',
db_column='depending_on',
blank=True, null=True)
object = models.ForeignKey('Objects', models.DO_NOTHING, related_name='fields')
object_database_column = models.ForeignKey(ObjectDatabaseColumns,
models.DO_NOTHING, blank=True,
null=True)
~~~
假如我们需要预存储所有指向叫account的Objects对象的ObjectFields对象,避免多次SQL查询,则正常的操作是
~~~py
obj = Objects.objects.prefetch_related('fields').get(name='account')
~~~
但是我们也可以用Prefetch对象来写
~~~py
obj = Objects.objects.prefetch_related(Prefetch('fields')).get(name='account')
~~~
不过如果Prefetch只是这么用,那它基本上就没有存在的意义了,现在来考虑一下更复杂的情况,假设现在需要得到account这个object下面所有的name以text开头的object_field,则正常的思路是
~~~py
obj = Objects.objects.prefetch_related(Prefetch('fields')).get(name='account')
obj_field_within_text = obj.fields.filter(name__icontains='text%') # obj.fields是一个RelatedManager对象,其行为和普通的object manager相似
~~~
的确这样可以得到想要的结果,但在这里有一个问题,使用prefetch_related的目的是为了预存储避免多次使用SQL语句,但这里的obj.fields实际上只对obj.fields.all()的数据进行了预存储,也就是说这里的filter会导致再次调用SQL语句,这种动态的SQL调用明显是应该被避免的,也就是这种情况下,Prefetch真正的作用显示出来了
上面这段代码如果利用Prefetch来处理的话可以变成下面这样
~~~py
obj = Objects.objects.prefetch_related(
Prefetch(
'fields',
queryset=ObjectFields.objects.filter(name__icontains='text%'),
to_attr='field_sets'
)
).get(name='account')
~~~
如果用这种方式处理,一样可以得到和上面一样的结果,同时我们这里还对text起头的条件进行了预处理,会在第一次调用这个QuerySet时一并完成,也就是说
~~~py
result = obj.field_sets
~~~
这样的调用不会再去查询数据库,因为结果已经被取出来了,而如果用上面的方式则还要去数据库取值
****
通过上面的讲解基本上就可以看出Prefetch对象的作用了:**在多对多或者多对一关系中提前对多个关联对象的QuerySet进行预筛选并存储起来,避免动态调用数据库**
#### 参考文章
1. [为什么django的prefetch_related不能对filter起作用?](https://stackoverflow.com/questions/12973929/why-does-djangos-prefetch-related-only-work-with-all-and-not-filter)
- Django基础
- 模型
- 外键
- Model Manager
- 过滤器函数
- 查询对象
- 字段的细节
- QuerySet的应用
- 视图
- Django类视图
- 权限控制
- Django进阶
- 中间件
- _meta组件
- 信号
- User模块
- prefetch_related和select_related的区别
- 较少被用到的查询对象
- Django的深层设计理念
- Declarative Syntax
- django的migration操作
- 较少用到的Queryset方法的一些坑
- Django配置
- Django环境配置变量
- Django源码阅读
- ORM
- QuerySet源码
- Query源码
- Q&F
- Model和Manager的详解
- Http请求响应
- HttpRequest
- 自建数据结构
- Django开发辅助工具
- Django-rest-framework
- Serializer
- 异步任务调度器Celery
- 数据库补充
- 定义
- 字段
- 事务
- 视图
- 函数
- 联结
- 窗口函数
- GROUPING运算符
- HAVING谓词
- django_bulk_update
- django_bulk_update源码分析
- 项目小功能开发
- Django的一些小细节