代码之家  ›  专栏  ›  技术社区  ›  Dan Breen

django foreignkey字段中的大量查询

  •  5
  • Dan Breen  · 技术社区  · 17 年前

    我整天都在为Django流口水,同时在创纪录的时间内编写一个内部网站,但现在我注意到我的外挂键在模型中效率很低。

    我有一个模型,它有6个foreignkey,基本上是查找表。当我查询所有对象并将它们显示在模板中时,每个项目大约运行10个查询。下面是一些代码,应该更好地解释它:

    class Website(models.Model):
        domain_name = models.CharField(max_length=100)
        registrant = models.ForeignKey('Registrant')
        account = models.ForeignKey('Account')
        registrar = models.ForeignKey('Registrar')
        server = models.ForeignKey('Server', related_name='server')
        host = models.ForeignKey('Host')
        target_server = models.ForeignKey('Server', related_name='target')
    
    class Registrant(models.Model):
        name = models.CharField(max_length=100)
    

    …还有5张简单的桌子。有155个网站记录,在视图中我使用:

    Website.objects.all()
    

    它最终执行了1544个查询。在模板中,我使用了所有的外部字段,如:

    <span class="value">Registrant:</span> <a href="/filter/registrant/{{ website.registrant.id }}">{{ website.registrant.name }}</a><br />
    

    所以我知道它会运行很多查询……但这似乎太过分了。这是正常的吗?我不应该这样做吗?

    我对姜戈很陌生,所以希望我只是在做些傻事。这绝对是一个非常棒的框架。

    1 回复  |  直到 17 年前
        1
  •  5
  •   Eli Courtwright    17 年前

    你应该使用 select_related function ,例如

    Website.objects.select_related()
    

    这样,在执行查询时,它将自动执行联接并跟踪所有这些外键,而不是在使用它们时按需加载它们。Django延迟地从数据库加载数据,因此默认情况下,您会得到以下行为

    # one database query
    website = Website.objects.get(id=123)
    
    # first time account is referenced, so another query
    print website.account.username 
    
    # account has already been loaded, so no new query
    print website.account.email_address
    
    # first time registrar is referenced, so another query
    print website.registrar.name
    

    等等。如果使用选定的“相关”,则在后台执行联接,并且在第一个查询上自动跟踪并加载所有这些外键,因此只执行一个数据库查询。所以在上面的例子中,你会得到

    # one database query with a join and all foreign keys followed
    website = Website.objects.select_related().get(id=123)
    
    # no additional query is needed because the data is already loaded
    print website.account.username
    print website.account.email_address
    print website.registrar.name