【问题标题】:How to select rows when there is no value in a related table?当相关表中没有值时如何选择行?
【发布时间】:2014-05-07 06:47:25
【问题描述】:

我有以下架构:

class ModelA(models.Model)
    sold = models.DateFiled(blank=True, null=True)
    enabled = models.BooleanField()

class ModelB(models.Model):
    model_a = models.ForeignKey(ModelA)
    start = models.DateField()
    end = models.DateField(blank=True, null=True, db_index=True)

我想获取所有ModelA 的实例,这些实例没有相关的任何 ModelBend 日期设置为Null,已启用而不是“出售”。 p>

目前我有以下查询集:

ModelA.objects.exclude(
    pk__in=ModelB.objects.filter(end__isnull=True).values('model_a_id')
).filter(
    enabled=True, sold__isnull=True
)

但是它的性能很差,有没有更好的方法来建模这个查询/查询集?

【问题讨论】:

  • 您使用的是什么类型的数据库?如果是 SQL,您是否考虑过构建原始 SQL 查询?只要表具有所述值,就可以在其中一个表上进行 LEFT JOIN 以返回行?
  • 这必须与数据库无关,目前这适用于 SQLite 和 PostgreSQL,尚未测试 MySQL。而且我看到 django 将第一个查询集内联到子查询中,但仍然表现不佳。

标签: python database django django-queryset


【解决方案1】:

您实际上可以过滤来自 ModelA 的 ModelB 关系,并让 Django 执行左外连接。以下查询应该执行连接而不是使用子查询:

from django.db.models import Q
q = ModelA.objects.exclude(Q(model_b__isnull=False) & Q(model_b__end__isnull=True))

在 django shell 中尝试上述操作,然后执行 print q.query 以查看它如何转换为 SQL。 Q() 语法允许您执行“或”过滤器。我们基本上是在说:给我所有没有的 ModelA:与 end__isnull=True 有 modelB 关系。

我认为该逻​​辑涵盖了您正在寻找的案例。否则,您可以执行类似的操作。

【讨论】:

  • 嗯...您看到了什么错误?我对此很有信心!
  • 没有错误,只是错误的结果,如果我有 2 个 ModelB 实例,指向 DB 中的同一个 ModelA,一个有结束日期,第二个没有结束日期,我不想包含这个 ModelA进入结果集,你的方式将包括它
  • 感谢您的澄清。我编辑了我的查询——现在就试试吧。否定“过滤器”所需的逻辑与我没有考虑的等效“排除”之间存在差异。
  • 这比我的第一个解决方案更糟糕,如果您添加启用过滤:ModelA.objects.exclude(...).filter(enabled=True) 使 django 生成 2 个子查询
  • 我添加了一些更多的细节问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 2015-06-17
  • 2017-08-29
  • 1970-01-01
  • 1970-01-01
  • 2023-02-15
相关资源
最近更新 更多