🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 较少被用到的查询对象 所谓查询对象,其实就是在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)