Django 合并多个 QuerySet

1 投票
1 回答
1459 浏览
提问于 2025-04-16 18:56

有没有办法把不确定数量的查询结果合并成一个列表呢?

这是我的模型:

class Item(models.Model):
    name = models.CharField(max_length=200)
    brand = models.ForeignKey(User, related_name='brand')
    tags = models.ManyToManyField(Tag, blank=True, null=True)
    def __unicode__(self):
        return self.name
    class Meta:
        ordering = ['-id']

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    def __unicode__(self):
        return self.name

我正在处理两种类型的查询:

  1. items = Item.objects.filter(brands__in=brands)

  2. items = Item.objects.filter(tags__name='80s').filter(tags__name='comedy')

关于第二种查询,用户可以保存搜索记录(比如“80年代喜剧”),并且可以同时保存多个搜索,所以我需要为他们保存的每个搜索创建一个查询。

我最开始想尝试构建一个可以处理这两种情况的单一查询(可以参考Django结合AND和OR查询的ManyToMany字段),但我现在觉得最好的办法是把所有查询合并成一个列表。

我喜欢@akaihola在这里的建议: 如何在Django视图中合并两个或多个查询集?,但我不知道如何用itertools.chain处理不确定数量的查询。

有没有人知道最好的办法来实现这个呢?

编辑:忘了提,我想要的是那些有特定品牌或者拥有所有所需标签的项目。

1 个回答

3

这个方法有点特别,但你可以用递归来解决问题。在你的例子中:

def recursive_search(tags, results_queryset):
    if len(tags) > 0:
        result_qs = result_queryset.filter(tags_name=tags[0])
        if result_queryset.exists():
            return filter_recursion(tags[1:],result_queryset)
        else:
            return None
    return result_queryset

tags = ["comedy", "80s", "action", "thriller"]  # This can be variable
result_queryset = Item.objects.filter(brands__in=brands) # Could be Item.objects.all()
print recursive_search(tags, result_queryset)

首先,你需要一个你要查找的标签列表,还有一个包含所有可能符合你条件的项目的查询集(在这个例子里,我们从某个品牌的所有商品开始)。

然后,你会逐个检查标签列表,并逐步缩小查询集。每次检查时,你都会重新过滤整个查询集,只保留那些包含所有提到的标签的项目。

具体来说:

  • 第一次检查会找出所有带有favorite标签的商品,
  • 第二次检查会找出同时带有favoriteloudest标签的商品,
  • 依此类推。

如果经过过滤后查询集返回的是None,那就意味着没有商品同时带有所有需要的标签,这时方法会停止并返回None(也就是说,它会在第一次失败时就停止)。而且,这样做应该只会对数据库进行一次查询(我想是这样的!)

我测试过这个方法,应该是可行的,试试看吧。

编辑

要把从品牌查询集(q1)和上面用itertools创建的查询集(q2)合并在一起:

list = []
for item in itertools.chain(q1, q2):
    list.append(item)

编辑 2

这样做不是可以在一次查询中完成你需要的操作吗?

# list of tags = ['comedy','80s']
qs = Item.objects.all( Q(brand__iexact="brand name") | Q(tags__name__in=[tag for tag in list_of_tags]) )

撰写回答