【问题标题】:Using django how can I combine two queries from separate models into one query?使用 django 如何将来自不同模型的两个查询组合成一个查询?
【发布时间】:2010-09-23 17:03:27
【问题描述】:

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

我们省略细节,只说第一种在一个叫Msg1的模型中,另一种叫Msg2

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

我可以得到Msg1.objects.all()Msg2.objects.all(),但是我可以将这两个查询合并为一个查询,按日期排序,然后分页吗?

我需要保留查询的惰性。

简单的解决方案是 list(query) 两个查询并将它们组合到一个 python 列表中。但这显然是低效的。

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

【问题讨论】:

  • “由于显而易见的原因效率低下” 真的吗?你有指标吗?我问是因为没有明显的原因会导致效率低下。
  • 我认为因为一旦你列出(查询)它就会得到所有结果,他想尽可能晚地离开。
  • 效率低下,因为它会访问所有项目的数据库(可能是 1000 个),而每页仅显示 20 个左右..
  • 查询处理相当复杂。实际测量是必不可少的。当 ORM 和数据库中存在缓存时,理论分析效果不佳。
  • 你可能是对的,起初我绝对可以使用 list(query) 来启动和运行页面,但从长远来看,当表开始有数千条记录时,它可能很多最好使用不会命中数据库中每条记录的解决方案。

标签: sql django django-models


【解决方案1】:

我建议您使用Model inheritance

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

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

【讨论】:

  • 如果我的基类是抽象的怎么办?你能展示一些代码示例吗?
【解决方案2】:

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

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

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

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


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

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

【讨论】:

  • 如果你走这条路,你会希望在 SQL 指令中的 union 之前执行排序(因为 SQL 可以使用索引来加速排序)并希望你可以限制中间排序结果在联合之前,减少任何临时数据的大小。
  • 但是,由于联合可以是表、计算、文字等的任何组合,联合必须创建并排序中间结果.也许你可以优化,但通用版本必须工作。
猜你喜欢
  • 2018-06-27
  • 2018-03-30
  • 2014-05-09
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 2018-08-07
  • 1970-01-01
  • 2019-02-11
相关资源
最近更新 更多