【问题标题】:Django AND query using Q objects使用 Q 对象的 Django AND 查询
【发布时间】:2021-09-15 05:59:37
【问题描述】:

假设我有一个Product 模型和一个一对多的ProductTag 模型。

class Product(models.Model):
    [...]


class ProductTag(models.Model):
    product = models.ForeignKey(Product)
    tag_value = models.CharField()
    [...]

如果我有 3 个带有一些标签的产品:

  1. 产品A [TagA]
  2. 产品B [标签B]
  3. 产品C [TagB/TagC]

我想动态查询带有一些标签的“产品”。

  1. 此查询仅返回“ProductA”(如预期的那样)。
Product.objects.filter(Q(producttag__tag_value="TagA"))
  1. 此查询返回所有 3 种产品(如预期的那样)。
Product.objects.filter(
    Q(producttag__tag_value="TagA") | Q(producttag__tag_value="TagB")
)
  1. 我希望以下查询只返回“ProductC”
Product.objects.filter(
    Q(producttag__tag_value="TagB") & Q(producttag__tag_value="TagC")
)

但是我得到一个空的查询集。为什么查询 #3 不起作用?

使用__in 查询也会返回错误结果(如预期)

Product.objects.filter(producttag__tag_value__in=["TagB", "TagC"])

上述查询同时返回 ProductB / ProductC。

更新

我使用 Q 对象进行查找的原因是查询基于来自 API 端点的用户输入。例如

  • "tag=TagA"
  • "tag=TagA AND tag=TagB"
  • "tag=TagA AND NOT tag=TagC"

所以我需要根据用户输入运行动态 Django 查询,并且无法对某些查询进行硬编码。

【问题讨论】:

  • 查询#3 有效地要求ProductTag.tag_value 在单行上同时等于TagB 和TagC,这显然是不可能的。我得到了您想要实现的目标,但仍在考虑可能的解决方案。这是 SQL 模型的结果,您总是根据表的单行进行过滤...

标签: python django django-models django-queryset


【解决方案1】:

以下查询工作:

qs1 = Product.objects.filter(Q(producttag__tag_value="TagB"))
qs2 = Product.objects.filter(Q(producttag__tag_value="TagC"))

queryset = qs1 & qs2

上面的查询只返回 ProductC。

大致基于这个答案https://stackoverflow.com/a/33271387/1682844

【讨论】:

  • 不建议使用它,它是 SQL INTERSECTION,Django 对它的支持很差(UNION 也是如此),您以后可能无法对查询进行一些修改。请参阅我的替代答案
  • 来自文档:此外,只有 LIMIT、OFFSET、COUNT(*)、ORDER BY 和指定列(即 slicing、count()、exists()、order_by() 和 values( )/values_list()) 在生成的 QuerySet 上是允许的。此外,数据库对组合查询中允许的操作设置了限制。例如,大多数数据库不允许在组合查询中使用 LIMIT 或 OFFSET。
  • 用更多细节更新了我的问题。查询是基于用户输入的动态查询,很难使用“Exists”之类的内容。
  • 好吧,没有什么能阻止你生成 Exists / ~Exists 查询,就像你生成 Q(producttag__tag_value="TagB") & Q(producttag__tag_value="TagC") 查询一样
  • 如果您需要有关如何实现这一目标的指导,请告诉我
【解决方案2】:

查询#3 有效地要求ProductTag.tag_value 在单行 上同时等于TagB 和TagC,这显然是不可能的。这是 SQL 查询语法/模型的结果,您总是基于表的单行进行过滤。

我没有想出比链接EXISTS 子句更漂亮、更高效的方法(仅适用于 Django 3.x):

rel_tag = ProductTag.objects.filter(product=OuterRef('pk'))
Product.objects.filter(
    Exists(rel_tag.filter(tag_value="TagB")) & 
    Exists(rel_tag.filter(tag_value="TagC"))
)

【讨论】:

  • 顺便说一句,我尝试使用EXISTS(使用 Django 2.2x)``` tag_query = ProductTag.objects.filter(product=OuterRef('pk')) qs = Product.objects.annotate(matched =Exists(tag_query.filter(tag_value="TagA")) & Exists(tag_query.filter(tag_value="TagB")) ).filter(matched=True) ``` Django 提出NotImplementedError: Use .bitand() and .bitor() for bitwise logical operations.
  • 是的,对,它只在 3.x 版本中修复。我将修改旧版本的解决方案
  • TypeError: 'Exists' 对象不可迭代。
  • 好吧,那你就完蛋了)唯一的方法是通过注释,但你必须给它命名,它会使动态查询构建复杂化
  • 感谢您迄今为止的帮助。
猜你喜欢
  • 2018-11-18
  • 2013-12-11
  • 2015-06-23
  • 2010-12-27
  • 2014-02-06
  • 2015-07-15
  • 2015-05-13
  • 2012-12-16
相关资源
最近更新 更多