5

我有以下模型结构:

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与特定 Container 有关系的所有类型模型。

用于此的 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)。

4

3 回答 3

2

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

实际上值得进行基准测试,看看哪些在现实中更快。第二个似乎更好,因为它(可能)查询更少,但是这些查询中的每一个都必须与 m2m 中间表执行连接。在前一种情况下,您只执行一个连接查询,然后执行许多简单的查询。一些数据库后端在处理大量小查询时比更少、更复杂的查询表现更好。

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

于 2009-10-29T14:39:53.257 回答
1

不是一个完整的答案,但您可以通过这样做避免大量点击

items= list(items)
for item in items:
    spec = getattr(item, item.get_my_specific_type())

而不是这个:

for item in items:
    spec = getattr(item, item.get_my_specific_type())

实际上,通过强制转换为 python 列表,您可以强制 django orm 加载查询集中的所有元素。然后它在一个查询中执行此操作。

于 2009-10-29T10:55:43.777 回答
0

我不小心偶然发现了以下帖子,它几乎回答了您的问题:

http://lazypython.blogspot.com/2008/11/timeline-view-in-django.html

于 2009-11-10T08:55:42.040 回答