获取具有M2M关系的所有Django对象
我有以下两种Django型号:获取具有M2M关系的所有Django对象,django,django-models,django-orm,django-3.0,Django,Django Models,Django Orm,Django 3.0,我有以下两种Django型号: class Parent(models.Model): name = models.CharField(max_length=50) children = models.ManyToManyField("Child", through="ParentChild") def __str__(self): return self.name class Child(models.Mode
class Parent(models.Model):
name = models.CharField(max_length=50)
children = models.ManyToManyField("Child", through="ParentChild")
def __str__(self):
return self.name
class Child(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
假设我的数据库中有四个Parent
对象,其中只有两个有子对象:
for parent in Parent.objects.all():
print(parent, parent.children.count())
# Parent1 3
# Parent2 0
# Parent3 0
# Parent4 1
我的目标是编写一个高效的数据库查询,以获取至少有一个孩子的所有家长。事实上,我有数以百万计的对象,所以我需要它尽可能地高效。到目前为止,我提出了以下解决方案:
prefetch\u相关的
过滤器
:排除:
注释
和排除
:以下哪种解决方案最快?是否有其他更快速/更具可读性的方法?我看到一个django函数,但它似乎不适用于此用例。与
.prefetch\u相关的(…)
将(可能)没有帮助,因为.exists()
将不使用预取,而是进行exists查询,从而导致N+1问题
您只需过滤存在非空子级的事实,并使用检索每个父级一次:
Parent.objects.filter(children__isnull=False).distinct()
以前,您可以使用.annotate(…)
,但这可能会降低效率:
from django.db.models import Exists, OuterRef
# before Django-3.0
Parent.objects.annotate(has_children=Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
)).filter(has_children=True)
从django.db.models导入存在,OuterRef
#Django-3.0之前
Parent.objects.annotate(has_children=Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
)).filter(has_children=True)
但是,确切的性能取决于数据库,因此最好对查询进行基准测试。它有时也取决于特定的数据库系统:MySQL数据库可以有不同于PostgreSQL数据库的基准。与预取相关的(…)可能没有帮助,因为
.exists()
不会使用预取,而是进行exists查询,从而导致N+1问题
您只需过滤存在非空子级的事实,并使用检索每个父级一次:
Parent.objects.filter(children__isnull=False).distinct()
以前,您可以使用.annotate(…)
,但这可能会降低效率:
from django.db.models import Exists, OuterRef
# before Django-3.0
Parent.objects.annotate(has_children=Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
)).filter(has_children=True)
从django.db.models导入存在,OuterRef
#Django-3.0之前
Parent.objects.annotate(has_children=Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
)).filter(has_children=True)
但是,确切的性能取决于数据库,因此最好对查询进行基准测试。有时还取决于特定的数据库系统:MySQL数据库可以有不同于PostgreSQL数据库的基准。这是一个相当开放的问题
- 首先,与所有优化一样:
- 测量基线性能(基于真实或现实数据),这样您就知道您改进了多少;及
- 确定哪些性能是令人满意的(从业务角度来看),或者您期望从其他性能中获得哪些好处
- 有两种方法:
- 在与基线相同的数据上测量所有变量。这是唯一确定的方法
- Django提供计算(或记录)所有查询的工具;你试过用这些吗?在大多数情况下,查询越少,速度越快,尤其是当数据库位于(或将位于)自己的服务器上时
- 查询完成后,使用EXPLAIN或SHOWPLAN在数据库中进行检查, 确保他们使用了适当的相关索引。为此,您需要真实或现实的数据。(优化数据库查询实际上是一个单独的问题;在这个问题中,您甚至没有说您正在使用哪个数据库…)
- 最后,当你达到目标时,停下来。不要在收益递减点或无业务影响点之外继续优化
- 首先,与所有优化一样:
- 测量基线性能(基于真实或现实数据),这样您就知道您改进了多少;及
- 确定哪些性能是令人满意的(从业务角度来看),或者您期望从其他性能中获得哪些好处
- 有两种方法:
- 在与基线相同的数据上测量所有变量。这是唯一确定的方法
- Django提供计算(或记录)所有查询的工具;你试过用这些吗?在大多数情况下,查询越少,速度越快,尤其是当数据库位于(或将位于)自己的服务器上时
- 查询完成后,使用EXPLAIN或SHOWPLAN在数据库中进行检查, 确保他们使用了适当的相关索引。为此,您需要真实或现实的数据。(优化数据库查询实际上是一个单独的问题;在这个问题中,您甚至没有说您正在使用哪个数据库…)
- 最后,当你达到目标时,停下来。不要在收益递减点或无业务影响点之外继续优化
- 这是一个相当开放的问题
Parent.objects.filter(children__isnull=False).distinct()
from django.db.models import Exists, OuterRef
# since Django-3.0
Parent.objects.filter(Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
))
from django.db.models import Exists, OuterRef
# before Django-3.0
Parent.objects.annotate(has_children=Exists(
ParentChild.objects.filter(parent_id=OuterRef('pk'))
)).filter(has_children=True)