如何使用Django prefetch_related

时间:2017-06-23 23:54:44

标签: django django-queryset

我的一个列表页面中有一个用于提供 unicode 功能的相关模型。 我试图预取以防止重复查询。

预取查询如下:

SELECT "pansys_modulex"."id", "pansys_modulex"."client", "pansys_modulex"."change_date", "pansys_modulex"."changed_by_id", "pansys_modulex"."create_date", "pansys_modulex"."created_by_id", "pansys_modulex"."language_id", "pansys_modulex"."short_text", "pansys_modulex"."module_id" FROM "pansys_modulex" WHERE ("pansys_modulex"."language_id" = '3' AND "pansys_modulex"."module_id" IN ('8', '9', '10', '12', '13', '14'))

尽管如此,我还是会收到如下的数十个重复查询:

SELECT "pansys_modulex"."id", "pansys_modulex"."client", "pansys_modulex"."change_date", "pansys_modulex"."changed_by_id", "pansys_modulex"."create_date", "pansys_modulex"."created_by_id", "pansys_modulex"."language_id", "pansys_modulex"."short_text", "pansys_modulex"."module_id" FROM "pansys_modulex" WHERE ("pansys_modulex"."language_id" = '3' AND "pansys_modulex"."module_id" = '8')

我在这里错过了什么? 我以为我会预取查询,如果可以的话,django会尝试使用预取结果。

编辑: 我的模型和预取命令本身。

#models trimmed

class Application(GenericCommonModel):
    module = models.ForeignKey(Module, verbose_name=_('Module'))

class Module(GenericCommonModel):
    def __unicode__(self):
        short_text = None
        try:
            language = PanBasLanguage.objects.get(language=get_language())
            short_text = ModuleX.objects.get(module = self,language = language).short_text
        except ModuleX.DoesNotExist:
            print 'ModuleX > DoesNotExist'
        return short_text or self.abbreviation

class ModuleX(models.Model):
    module = custom_model_fields.PanNotNullForeignKey(Module, on_delete=models.CASCADE)
    language = custom_model_fields.PanNotNullForeignKey('panbas.PanBasLanguage')
    short_text = custom_model_fields.PanShortTextField()
    class Meta:
        verbose_name = _('Module Description')
        verbose_name_plural = _('Module Descriptions')
        unique_together = ('module', 'language')

class PanBasLanguage(GenericBasicModel):
    language_choices = settings.LANGUAGES
    language = custom_model_fields.PanNoneBlankCharField(choices=language_choices, max_length=8,
                                                         verbose_name=_('Language'), unique=True)


#in my view     

language = PanBasLanguage.objects.get(language=get_language())
x_result = ModuleX.objects.filter(language=language)
prefetch = Prefetch('module__modulex_set', queryset=x_result)
queryset = queryset.prefetch_related(prefetch)

简而言之,语言模型有可用的语言,Module有ModuleX模型来保持不同语言的short_texts。在我的应用程序列表中,我打印出一个列中的模块,这个模块单独进行模块查找。

2 个答案:

答案 0 :(得分:2)

  

简而言之,语言模型有可用的语言,Module有ModuleX模型来保持不同语言的short_texts。在我的应用程序列表中,我打印出一个列中的模块,这个模块单独进行模块查找。

所以你要找的是模块对象,你想要用给定语言的短文本。

现在,从Module对象接近它然后处理反向外键是很自然的。但是,由于语言限制将始终在关系的许多方面生成一个结果,因此最好从多方面处理此问题,就像现在一样。作为奖励,您可以使用select_related作为Escher指出。

根据Module上的过滤的复杂程度,您可能希望使用两个查询(仍然消除循环内的查询)。

所以,这是一种方式:

ModuleX.objects.filter(language__language=get_language(), **module_filters).select_related('module')

其中**module_filters是对模块的过滤。你可以用一个小包装器来做到这一点:

def _get_module_filters(self, **kwargs):
    filters = {}
    for k, v in **kwargs:
        filters['module__' + k] = v
    return filters

这节省了一些输入,并允许您专注于查询模块而不是语言包装器。

两种查询方式是:

modules = Module.objects.filter(**module_filters)
translations = ModuleX.objects.filter(language__language=get_language(),
    module__in=modules).select_related('module')

请注意,如果您在模块实例上调用str(),则会再次获得其他查询,因为您在那里执行相同的工作。这包括模板代码,如:

{% for obj in translations %}
     {{ obj.module }}
{% endfor %}

translations是上述查询集。

答案 1 :(得分:1)

来自the docs

  

select_related(* fields)返回一个“跟随”外键关系的QuerySet,在执行查询时选择其他相关对象数据。这是一个性能提升器,它会导致单个更复杂的查询,但意味着以后使用外键关系不需要数据库查询。

  另一方面,

prefetch_related 对每个关系进行单独查找,并在Python中进行“加入”。这允许它预取多对多和多对一对象,除了select_related支持的外键和一对一关系之外,这些对象无法使用select_related完成。

鉴于您的是外键关系,而不是m2m,您应该使用i进行该查询。