Django 一对多关系:优化代码以减少执行的数据库查询数

Django one-to-many relation: optimize code to reduce number of database queries executed

我有 2 个模型在 MySQL 数据库上具有一对多关系:

class Domains(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(blank=True, null=True)

class Kpis(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(blank=True, null=True)
    domain_id = models.ForeignKey(Domains, on_delete=models.CASCADE, db_column='domain_id')

为了将所有域与它们的所有 kpis 对象一起使用,我将此代码与 for 循环一起使用:

final_list = []
domains_list = Domains.objects.all()
for domain in domains_list:
    # For each domain, get all related KPIs
    domain_kpis = domain.kpis_set.values()
    final_list.append({domain:domains_kpis})

我的查询总数运行是:1+我拥有的总域名数,这个数目不少。

我正在寻找一种优化它的方法,最好是只在数据库的一个查询中执行它。这可能吗?

您为此使用 .prefetch_related(…) [Django-doc]

final_list = []
domains_list = Domains.objects<b>.prefetch_related('kpis_set')</b>
for domain in domains_list:
    # For each domain, get all related KPIs
    domain_kpis = domain.kpis_set<b>.all()</b>
    final_list.append({domain:domains_kpis})

这将进行两次查询:一次查询域,第二次查询所有相关 Kpis 并在内存中进行一次查询。

此外请不要使用.values()。您可以使用 Django 的序列化器框架将数据序列化为 JSON,通过使用 .values() 可以“侵蚀”模型层。见 Serializing Django objects section of the documentation for more information.

只是想补充一点,您正在寻求“经典”N +1 查询问题的解决方案。 Here 您可以阅读一些相关内容,还可以找到 Willem 的回答中建议的 prefetch_related 方法示例。

另一件值得一提的事情是,您可能不打算使用此字典 final_list.append({domain:domains_kpis}),而是您可能希望将某些字段从 Domain 映射到某些字段(s ) 来自 Kapis 模型,如果这是真的,您可以使用 Prefetch:

指定您想要预取的确切字段
domains_list = Domains.objects.prefetch_related(Prefetch('kpis_set'), queryset=Kapis.objects.all().only('some_field_you_want_to_have'))
final_list = []
for domain in domains_list:
    domain_kpis = domain.kpis_set.all()
    final_list.append({domain.some_field:domains_kpis.prefetched_field})

这应该会再次提升大容量 table 的性能。