使用django如何将来自不同模型的两个查询组合成一个查询?

时间:2008-11-23 23:58:18

标签: sql django django-models

在我的具体情况中,我需要两种“消息”来进行检索和分页。

让我们省略细节,然后说第一种是在名为Msg1的模型中,另一种叫做Msg2

这两个模型的字段完全不同,两个模型共有的唯一字段是“日期”和“标题”(当然还有id)。

我可以获得Msg1.objects.all()Msg2.objects.all()但是我可以将这两个查询合并到一个查询中,按日期排序并对其进行分页吗?

我需要保留查询的惰性。

简单的解决方案是list(query)两个查询并将它们组合在一个python列表中。但由于显而易见的原因,这种效率很低。

我查看了模型和dp-api上的django引用,但似乎没有办法将不同模型/表的查询合并为一个。

2 个答案:

答案 0 :(得分:11)

我建议您使用Model inheritance

创建包含日期和标题的基本模型。如上所述,子类Msg1和Msg2关闭它。使用基本模型完成所有查询(填充页面),然后在最后一刻切换到派生类型。

继承的真正好处在于,django允许您在其他模型的外键中使用基本模型,因此您可以使整个应用程序更加灵活。在引擎盖下,它只是一个基本模型的表,每个子模型包含一对一键的表。

答案 1 :(得分:2)

“将这两个查询合并为一个查询,按日期排序,并对其进行分页?”

  1. 这就是SQL联盟。离开Django ORM并使用SQL联合。它的速度并不快,因为SQL必须创建一个临时结果,它会进行排序。

  2. 创建可以排序的临时结果。由于列表具有排序方法,因此您必须将两个结果合并为一个列表。

  3. 编写一个接受两个查询集的合并算法,对结果进行分页。


  4. 编辑。这是一个合并算法。

    def merge( qs1, qs2 ):
        iqs1= iter(qs1)
        iqs2= iter(qs2)
        k1= iqs1.next()
        k2= iqs2.next()
        k1_data, k2_data = True, True
        while k1_data or k2_data:
            if not k2_data:
                yield k1
                try:
                    k1= iqs1.next()
                except StopIteration:
                    k1_data= False
            elif not k1_data:
                yield k2
                try:
                    k2= iqs2.next()
                except StopIteration:
                    k2_data= False
            elif k1.key <= k2.key:
                yield k1
                try:
                    k1= iqs1.next()
                except StopIteration:
                    k1_data= False
            elif k2.key < k1.key: # or define __cmp__.
                yield k2
                try:
                    k2= iqs2.next()
                except StopIteration:
                    k2_data= False
            else:
                raise Exception( "Wow..." )
    

    你可以折叠分页:

    def paginate( qs1, qs2, start=0, size=20 ):
        count= 0
        for row in merge( qs1, qs2 ):
            if start <= count < start+size:
                yield row
            count += 1
            if count == start+size:
                break