Django Q过滤器,无法在单个查询中获得结果

Posted

技术标签:

【中文标题】Django Q过滤器,无法在单个查询中获得结果【英文标题】:Django Q filter, cant get results in a single query 【发布时间】:2021-06-10 07:44:16 【问题描述】:

我想使用多个条件进行查询以从我的 Order 模型中获取一些对象,但我找不到在单个查询中获取所有结果的方法。除了进行 2 个查询的选项外,我想知道这是否可以只用一个查询,这样我就可以创建一个包含所有这些订单的 CSV 表(这部分已经有效,所以我会坚持查询)。 条件:

支付方式:Paypal 和 Mollie created_at:在 15:00 和 16:00 小时内

&

支付方式:ApplePay created_at:在 17:00 和 18:00 小时内

两个查询:

Order.objects.all() \
.filter(Q(paymethod="Paypal") | 
        Q(paymethod="Mollie") & 
        Q(created_at__hour__in=(15, 16)))


Order.objects.all() \
.filter(Q(paymethod="ApplePay") 
        Q(created_at__hour__in=(17, 18)))

这两个查询单独工作正常,但我想知道是否可以将它们组合成一个查询。

我尝试过类似的方法:

Order.objects.all() \
.filter(Q(paymethod="Paypal" | "Mollie", created_at__hour__in=(15, 16)) \
& Q(paymethod="ApplePay", created_at__hour__in=(17, 18)))

上述方法不起作用,因为:类型错误:|: 'str' 和 'str' 的操作数类型不受支持。 所以我尝试了 paymethod="Paypal" | "Mollie" 而不是 paymethod="Paypal" | paymethod="Mollie" 但不幸的是,这也不起作用..

如果有人能指出我正确的方向,我将不胜感激。我还在学习 django,django Q 对我来说是新的。如果需要任何其他信息,请告诉我!谢谢!

【问题讨论】:

您显示的第一个查询已经走在正确的轨道上。为什么不像在第一个查询中那样在 Q 对象上简单地使用 |&?如果这是运算符优先级的问题,请尝试使用括号。 感谢您的评论。我知道我在第一次查询时很接近,但它无法解决。 (可能是因为睡眠不足..)但是由于下面的答案,它现在可以工作了! 【参考方案1】:

我认为您根本不需要使用Q。这个用例应该被QuerySet.filterQueryset.union 覆盖。

Order.objects.filter(
    paymethod__in=["Paypal", "Mollie"], 
    created_at__hour__in=[15, 16],
).union(
    Order.objects.filter(
        paymethod="ApplePay", 
        created_at__hour__in=[17, 18],
    )
)

当您将多个参数传递给filter 时,django 将在生成的 sql 语句中使用 sql AND。这相当于将您的 Q 对象与集合交集运算符 & 连接起来。

QuerySet.union 方法等价于查询集上的| 运算符 并将转换为 sql UNION。 (Q 对象上的 | 运算符将转换为 sql OR 操作。)

接受param__in=[...] 的Django 查询集方法会将这些参数转换为sql IN 语句。这应该等同于使用Q | Q(给出相同的结果)。

filter(paymethod__in=["Paypal", "Mollie"])
# produces same results as 
filter(Q(paymethod="Paypal") | Q(paymethod="Mollie"))

我怀疑在典型情况下 IN 会更快,但您应该有基准来确认这一点。

【讨论】:

union 没有使用 SQL OR,它实际上使用了 SQL UNION 运算符。 @AbdulAzizBarkat:你是对的。谢谢指正。 非常感谢先生!我现在可以使用Queryset.union :)【参考方案2】:

您可以进一步嵌套子查询,如下所示:

Order.objects.filter(
    Q(
        Q(paymethod="Paypal") | 
        Q(paymethod="Mollie") & 
        Q(created_at__hour__in=(15, 16))
    ) |
    Q(
        Q(paymethod="ApplePay") 
        Q(created_at__hour__in=(17, 18))
    )
)

更新: 此外,如果您出于某种原因不想使用“Q”对象,您可以“合并”查询集:

q1 = Order.objects.all() \
.filter(Q(paymethod="Paypal") | 
        Q(paymethod="Mollie") & 
        Q(created_at__hour__in=(15, 16)))

q2 = Order.objects.all() \
.filter(Q(paymethod="ApplePay") 
        Q(created_at__hour__in=(17, 18)))

result = q1 | q2

【讨论】:

【参考方案3】:

您可以使用嵌套的 Q 对象来做到这一点,如下所示:

Order.objects.filter(
    Q(
        Q(paymethod="Paypal") | Q(paymethod="Mollie") & Q(created_at__hour__in=(15, 16))
    ) |
    Q(
        Q(paymethod="ApplePay") & Q(created_at__hour__in=(17, 18))
    )
)

【讨论】:

以上是关于Django Q过滤器,无法在单个查询中获得结果的主要内容,如果未能解决你的问题,请参考以下文章

Django学习系列(二,Models数据操作篇)

Django 查询:返回一个将转换为“AND (...)”的 Q 对象

Django - 按最大(日期)年份过滤查询集

在 django 查询集中的两个日期之间有可能获得几个月?

Django过滤查询和或

Django 数据库操作