在 Django 中查询 ManyToMany 给我重复的结果

qwe*_*i90 3 python django python-2.7 django-1.9

我有两个模型

class Tag(models.Model):
   key = models.CharField(max_length=200)

class Post(models.Model):
   name = models.CharField(max_length=200)
   tags = models.ManyToManyField(Tag)
Run Code Online (Sandbox Code Playgroud)

我正在尝试过滤带有标签列表的帖子。让我们说标签heatwarm。我将在我的 api 函数 ( ['heat', 'warm']) 中获得一个标签列表。我想过滤所有包含其键在列表中的标签的 Post 数据。我尝试了很多类型,但没有得到正确的输出。有没有办法在单个查询中做到这一点?

Leo*_*nes 7

虽然Wilfried 的答案是正确的,并且对于很多用例来说都是完美的,但值得注意的是,在 SQL 级别上,可能DISTINCT会对性能产生影响,特别是如果您希望查询匹配大部分数据(取决于数据库和表)尺寸;请参阅此处此处了解更多详细信息)。

另一个稍微详细一点的选项可以避免这个陷阱,那就是使用throughmodel 和 a Subquery(在 django 1.11 中引入)。基于OP代码:

class Tag(models.Model):
    key = models.CharField(max_length=200)

class Post(models.Model):
    name = models.CharField(max_length=200)
    tags = models.ManyToManyField(Tag, through='Tagging')

class Tagging(models.Model):
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
Run Code Online (Sandbox Code Playgroud)

以及相应的查询:

tags = Tagging.objects.filter(tag__key__in=['heat', 'warm'])
Post.objects.filter(pk__in=models.Subquery(tags.values('post__pk')))
Run Code Online (Sandbox Code Playgroud)

使用.explain(analyze=True)(在 django 2.1 中引入)将帮助您做出明智的决定。


Wil*_*ied 6

具有此标签键的所有帖子等于heatwarm

Post.objects.filter(tags__key_in=['heat', 'warm']) 
Run Code Online (Sandbox Code Playgroud)

添加 distinct 以避免重复:

Post.objects.filter(tags__key_in=['heat', 'warm']).distinct()
Run Code Online (Sandbox Code Playgroud)