【问题标题】:Always False Q object总是 False Q 对象
【发布时间】:2016-06-23 23:05:30
【问题描述】:

在 Django ORM 中,如何创建一个始终为 False 的 Q 对象?

这与always True Q objects的问题类似,但反过来。

请注意,这不起作用:

Foobar.objects.filter(~Q()) # returns a queryset which gives all objects

为什么我想要一个 Q 对象而不是简单的 False 值?这样我就可以将它与其他 Q 值结合起来,例如:

condition = always_true_q_object
if something_or_other:
    condition = condition | foobar_that_returns_a_q_object()
if something_or_other2:
    condition = condition | foobar_that_returns_a_q_object2()

【问题讨论】:

  • 你的意思是.exclude(Q())?..
  • @Sayse 我可以看到它是如何工作的。但是,我想要一个始终为 False 的 Q 对象,以便我以后可以使用它与具有 AND 和 OR 逻辑的其他 Q 对象组合。
  • 如果您可以显示 minimal reproducible example 确切地说明您要查找的内容,这可能会有所帮助
  • 为什么您想要一个始终评估为 False 的查询而不是使用 False 本身?
  • 根据我的经验,这是很常见的模式。在 ORM 领域之外,我已经看到在 MySQL 查询中用于此目的的 1=01=1。我不能简单地在 Django 中做到这一点似乎很愚蠢。

标签: django


【解决方案1】:

使用Q(pk__in=[]) 似乎是表示这个成语的好方法。

如下面的@fwip 和 cmets 所示:Django 的 ORM 很好地识别了这种情况,知道这总是评估为 FALSE。例如:

FooBar.objects.filter(Q(pk__in=[]))

正确返回一个空的QuerySet,而不涉及到数据库的任何往返。而:

FooBar.objects.filter(
  (Q(pk__in=[]) & Q(foo="bar")) |
  Q(hello="world")
)

优化为:

FooBar.objects.filter(
  Q(hello="world")
)

即它识别出Q(pk__in=[]) 始终是FALSE,因此AND 条件永远不可能是TRUE,因此被删除。

要查看实际发送到数据库的查询,请参阅:How can I see the raw SQL queries Django is running?

【讨论】:

    【解决方案2】:

    注意:Sam 的回答更好。我将这个答案留在这里而不是删除它,以便您可以看到 Sam 所指的“更老套”的答案

    原答案:

    怎么样:

    Q(pk__isnull=True)
    

    Q(pk=None)
    

    它看起来很hacky,但它似乎工作。例如:

    >>> FooBar.objects.filter(Q(x=10)|Q(pk__isnull=True))
    [<FooBar: FooBar object>, ...]
    >>> FooBar.objects.filter(Q(x=10)&Q(pk__isnull=True))
    []
    

    但是,请注意,当与空的 Q() 进行 OR 运算时,它不会像您预期的那样工作。

    >>> FooBar.objects.filter(Q()|Q(pk__isnull=True))
    []
    

    解决此问题的方法可能是使用Q(pk__isnull=False) 作为“始终正确的 Q”。

    >>> FooBar.objects.filter(Q(pk__isnull=False)|Q(pk__isnull=True))
    [<FooBar: FooBar object>, ...]
    >>> FooBar.objects.filter(Q(pk__isnull=False)&Q(pk__isnull=True))
    []
    

    【讨论】:

    • 应该注意的是,“当与空的Q() 进行或运算时,它不会像您预期的那样工作”不是因为Q(pk=None) 方法的限制,而仅仅是因为@987654330 @ 不是“真正的 Q 对象”。它是“一个空的 Q 对象”。它不会向系统添加任何新信息。它不会改变任何东西。 Q()|[anything] 等价于 [anything]
    • @LudwikTrammer 这很好地解释了为什么它不起作用。所以我认为一个可能的解决方案可能是使用Q(pk__isnull=False) 而不是Q() 作为'True Q object'。
    • 这个答案可能不应该被Q(pk__in=[]) 答案接受。 Django 优化器(正确)无法将 Q(pk__isnull=True)Q(pk=None) 识别为始终返回空的表达式。 pk 字段可以被覆盖,因此这不是一个安全的假设。这导致查询比其他答案更混乱。
    【解决方案3】:

    我没有足够的声誉发表评论,但 Sam Mason 的回答 (Q(pk__in=[])) 的优势在于,如果单独使用它甚至不会执行数据库查询。 Django (v1.10) 似乎足够聪明,可以识别条件不可满足,并在不询问数据库的情况下返回一个空查询集。

    $ ./manage.py shell_plus
    
    In [1]: from django.db import connection
    
    In [2]: FooBar.objects.filter(Q(pk__in=[]))
    Out[2]: <QuerySet []>
    
    In [3]: connection.queries
    Out[3]: []
    

    【讨论】:

    • 当它只是一个评论时,不要误用答案按钮。所以也许你想再改进一点......把它变成一个真正的答案。
    • 好的,但是 Sam 先回答了这个问题,我只是对他的回答进行了补充。我真的不知道你所说的“提高一点”是什么意思——我的声誉?我的知识?我只是想添加一个有用的花絮,以便其他人受益。
    • 很高兴注意到优化器将正确地简化复杂的表达式。查询( Q(pk__in=[]) &amp; Q(foo="bar") ) | Q(hello="world") 会将条件简化为WHERE "hello" = world。它也适用于波浪号~ 否定。
    猜你喜欢
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-08
    相关资源
    最近更新 更多