如何在Django中使用Q.AND限制ManyToMany关系的查询

5 投票
1 回答
1720 浏览
提问于 2025-04-16 11:12

我想获取所有同时拥有两个特定标签的图片,分别是'tag1'和'tag2'。以下是我简化后的模型:

class Image(models.Model):
    title = models.CharField(max_length=100)

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    images = models.ManyToManyField(Image, null=True, blank=True)

使用filter连接的方法是有效的:

query = Image.objects.filter(tag__name='tag1').filter(tag__name='tag2')

不过,我本以为可以用Django中的Q对象来实现。因为我正在构建一个复杂的查询,使用Q会更简单。我通过qobj = Q()来添加所有参数,使用qobj.add(Q(tag__name='tag1'), Q.AND)。但是……接下来的查询没有返回任何结果:

qobj = Q()
qobj.add(Q(tag__name='tag1'), Q.AND)
qobj.add(Q(tag__name='tag2'), Q.AND)
query = Image.objects.filter(qobj)

当我在上面的代码中使用OR连接时,一切都按预期工作,正确返回了同时拥有tag1或tag2的图片。

看起来在AND的情况下,它在查找app_tag_images表中同时拥有这两个标签的记录,但显然没有这样的记录,因为每一行只对应一个标签ID和一个图片ID。

有没有办法用Q来构建这个查询呢?

附注:如果需要更多代码细节,请告诉我。

编辑:

这是使用Q构建的查询的SQL语句(我为了清晰起见,简化了大部分SELECT列):

SELECT "meta_image"."id", "meta_image"."title"
FROM "meta_image"
INNER JOIN "meta_tag_images" ON ("meta_image"."id" = "meta_tag_images"."image_id")
INNER JOIN "meta_tag" ON ("meta_tag_images"."tag_id" = "meta_tag"."id")
WHERE ("meta_tag"."name" = tag1 AND "meta_tag"."name" = tag2)

使用OR的查询与上面的查询相同,只是把AND换成了OR

为了参考,使用连接filter的方法生成的查询(同样简化)如下:

SELECT "meta_image"."id", "meta_image"."title"
FROM "meta_image"
INNER JOIN "meta_tag_images" ON ("meta_image"."id" = "meta_tag_images"."image_id")
INNER JOIN "meta_tag" ON ("meta_tag_images"."tag_id" = "meta_tag"."id")
INNER JOIN "meta_tag_images" T4 ON ("meta_image"."id" = T4."image_id")
INNER JOIN "meta_tag" T5 ON (T4."tag_id" = T5."id")
WHERE ("meta_tag"."name" = tag1 AND T5."name" = tag2)

1 个回答

0

我之前都不知道有这种格式!

文档中关于Q对象用法的说明有什么问题吗? http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects

Image.objects.filter(Q(tag__name='tag1') & Q(tag__name='tag2'))

更新:
我在我的模型上测试了qobj.add()方法,使用多对多关系(m2m),在1.2.3版本上运行得很好。

你简化后的模型复制粘贴也能正常工作。

你确定你的查询应该返回一些结果吗?

标准的Q用法 Q(tag__name='tag1') & Q(tag__name='tag2') 能返回结果吗?

你能打印一下 myquery.query 吗?

让我们把问题缩小一下。

撰写回答