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 的性能。
我有 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 的性能。