【问题标题】:How to use Django prefetch_related如何使用 Django prefetch_related
【发布时间】:2017-11-27 14:55:27
【问题描述】:

我的一个列表页面有一个提供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)

简而言之,Language 模型有可用的语言,Module 有 ModuleX 模型来保存不同语言的短文本。在我的应用程序列表中,我在列中打印出模块,并且该模块单独进行 modulex 查找。

【问题讨论】:

  • 请添加您构建的实际 Django 查询集和模型。随意修剪模型以仅显示关系字段,以便我们查看关系。
  • 编辑了我的问题。
  • 里面有些东西是不透明的,比如“Prefetch”类。但最突出的是 unicode 方法。如果您只是返回单个模型字段或将其全部删除会发生什么?
  • 我返回了一个字段,所有的 modulex 重复项都消失了。如果我删除预取类并简单地做 queryset.prefetch_related('module__modulex_set') ,结果是一样的。我使用 Prefetch 类首先使用活动语言过滤 modulex 结果。

标签: django django-queryset


【解决方案1】:

简而言之,Language 模型有可用的语言,Module 有 ModuleX 模型来保存不同语言的短文本。在我的应用程序列表中,我在列中打印出模块,并且此模块单独进行 modulex 查找。

所以您正在寻找的是模块对象,并且您想要给定语言的短文本。

现在,很自然地从 Module 对象处理它,然后处理反向外键。但是,由于语言限制总是会在关系的 ma​​ny 方面生成 one 结果,因此最好从 Many 方面处理这个问题,就像现在一样。作为奖励,您可以使用 Escher 指出的 select_related

根据 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 是上面的查询集。

【讨论】:

    【解决方案2】:

    来自the docs

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

    另一方面,

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

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

    【讨论】:

    • 如果他正在寻找 Module 对象,则不会。那么 ModuleX 就是反向外键。
    • 呃,我没有意识到他/她正在尝试“反向”查询。我认为目标是打印该语言的语言表和相应的文本,但我不确定。没有足够的上下文来确定模型架构甚至是明智的。
    猜你喜欢
    • 1970-01-01
    • 2016-11-28
    • 2015-06-08
    • 2020-08-27
    • 2013-01-24
    • 2012-10-07
    • 2015-02-10
    • 1970-01-01
    • 2013-09-26
    相关资源
    最近更新 更多