【问题标题】:Django: Filter on related model's field not being equal to a specific valueDjango:过滤相关模型的字段不等于特定值
【发布时间】:2021-12-27 03:10:43
【问题描述】:

鉴于这些模型:

from django.db import models

class Foo(models.Model):
    pass  # table with only one column, i.e. a primary key 'id' of type integer

class Bar(models.Model):
    value = models.TextField()
    foo = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name='bars')

如何创建一个过滤器,返回与至少一个 Bar 相关的所有Foos 以及非零 value

我知道可以首先选择非零的Bars,然后返回与它们相关的Foos。但我特意在寻找一种直接在 QuerySet 中获取相关Foos 的解决方案。原因是它可以很容易地应用于嵌套更深层次的关系。


什么不起作用

创建一个查询集很简单,它返回与至少一个Bar 相关的所有Foos, value

Foo.objects.filter(bars__value="zero")

但是,由于 Django 的过滤器方法中缺少“不等于”运算符,所以不可能做这样的事情:

Foo.objects.filter(bars__value__not_equal="zero")  # Unsupported lookup 'not_equal' for TextField

使用exclude:

Foo.objects.exclude(bars__value="zero")

不会产生所需的输出,因为它排除了所有与至少一个具有零值的Bar 相关的Foos。

所以,如果一个Foo 与两个Bars 相关,其中一个value 为0,另一个value 为非零值,则会被此exclude 过滤掉.然而,我们的意图是包含它,因为它与至少一个具有非零值的Bar 相关。

同样,使用取反的Q 对象:

Foo.objects.filter(~models.Q(bars__value="zero"))

也不会产生所需的输出,因为它只会包括所有Foos,这些Foos 任何 Bars 相关且值为零。

它将创建与上述exclude 方法相同的查询集。


注意事项

B.value 字段的类型已被有意选择为示例中的文本,以强调我正在寻找一个通用解决方案,其中无法通过使用例如模拟“不等于”运算符。 “小于”和“大于”运算符的组合。

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    我也想不出一个简单的方法来实现这一点,但使用Subquery 排除零值Bars 可能会起作用:

    from django.db.models import Subquery
    
    qs = Foo.objects.filter(
        bars__pk__in=Subquery(Bar.objects.exclude(value="zero").values('pk'))
    )
    

    这应该给你所有Foo 实例与Bar 具有非零值的关系。

    【讨论】:

    • 谢谢。这可行,但是检查子查询中的所有Bars 效率低下,尤其是当Foo 对象的外部QuerySet 已经受到一些其他过滤条件的限制时。如果可能的话,我想使用一种在这些情况下也相当有效的方法。
    • 请注意,子查询永远不会像普通查询集那样被评估。它仅作为指令传递给能够有效执行查询的数据库,特别是如果您设置了适当的索引。如果您找到一个满足所有要求的性能更高的解决方案,我将不胜感激。
    猜你喜欢
    • 2020-08-15
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2018-03-01
    • 2016-07-23
    • 1970-01-01
    相关资源
    最近更新 更多