获取具有M2M关系的所有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

我有以下两种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.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)