我具有以下模型结构:

class Container(models.Model):
    pass

class Generic(models.Model):
    name = models.CharacterField(unique=True)
    cont = models.ManyToManyField(Container, null=True)
    # It is possible to have a Generic object not associated with any container,
    # thats why null=True

class Specific1(Generic):
    ...

class Specific2(Generic):
    ...

...

class SpecificN(Generic):
    ...

说,我需要检索所有与特定容器有关系的Specific型模型。

SQL或多或少是无关紧要的,但这不是问题。不幸的是,我在使用ORM(尤其是Django的ORM)方面经验不足,因此我可能在这里缺少模式。

当以蛮力方式完成时,-
c = Container.objects.get(name='somename') # this gets me the container
items = c.generic_set.all()
# this gets me all Generic objects, that are related to the container
# Now what? I need to get to the actual Specific objects, so I need to somehow
# get the type of the underlying Specific object and get it
for item in items:
    spec = getattr(item, item.get_my_specific_type())

这会导致大量的数据库命中(每个通用记录一个,与容器相关),因此,显然这不是实现此目的的方法。现在,也许可以通过直接获取SpecificX对象来完成:
s = Specific1.objects.filter(cont__name='somename')
# This gets me all Specific1 objects for the specified container
...
# do it for every Specific type

这样,每种特定类型的数据库都会被命中一次(我猜是可以接受的)。

我知道.select_related()不适用于m2m关系,因此在这里并没有太大帮助。

重申一下,最终结果必须是SpecificX对象的集合(不是Generic)。

最佳答案

我认为您已经概述了两种简单的可能性。您可以针对Generic进行单个过滤查询,然后将每个项目转换为其Specific子类型(n + 1个查询的结果,其中n是返回的项目数),或者针对每个Specific表进行单独的查询(k的结果)查询,其中k是特定类型的数量)。

值得一提的是,看看其中哪一个速度更快。第二个似乎更好,因为它(可能)减少了查询,但是这些查询中的每个查询都必须与m2m中间表执行联接。在前一种情况下,您只执行一个联接查询,然后执行多个简单联接查询。对于许多小型查询,某些数据库后端的性能要好于较少,更复杂的后端。

如果第二个对于您的用例而言实际上要快得多,并且您愿意做一些额外的工作来清理代码,则应该可以为Generic模型编写一个自定义管理器方法,以“预取”所有给定查询集的相关特定表中的子类型数据,每个子类型表仅使用一个查询;类似于this snippet如何通过批量预取来优化通用外键。使用第一个选项的DRYer语法,这将为您提供与第二个选项相同的查询。

关于Django ORM : Optimizing queries involving many-to-many relations,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1642092/

10-12 22:17