代码之家  ›  专栏  ›  技术社区  ›  shylent

Django ORM:优化涉及多对多关系的查询

  •  5
  • shylent  · 技术社区  · 15 年前

    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())
    

    s = Specific1.objects.filter(cont__name='somename')
    # This gets me all Specific1 objects for the specified container
    ...
    # do it for every Specific type
    

    这样,对于每个特定类型,db将被命中一次(我想可以接受)。

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

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

    3 回复  |  直到 15 年前
        1
  •  2
  •   Carl Meyer    15 年前

    我想你已经概述了两种简单的可能性。要么对泛型执行单个筛选查询,然后将每个项强制转换为其特定的子类型(结果为n+1查询,其中n是返回的项数),要么对每个特定表执行单独的查询(结果为k查询,其中k是特定类型的数)。

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

    this snippet

        2
  •  1
  •   madewulf    15 年前

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

    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加载queryset中的所有元素。然后,它在一个查询中执行此操作。

        3
  •  0
  •   madewulf    15 年前

    我无意中发现了以下帖子,它基本上回答了你的问题:

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