gpt4 book ai didi

python - 根据ManyToMany关系存在过滤Django QuerySet

转载 作者:太空宇宙 更新时间:2023-11-03 15:07:29 28 4
gpt4 key购买 nike

模型

这是我们的基本模型设置。

一个列表有许多项目,一个项目可以在多个列表中。对于给定的项目,如果其列表中的任何都是(即list.bad == False),那么该项目就是好。如果某个项目没有出现在任何列表中,那么它就是

我们有一个用于项目的自定义查询集,其中有一个仅返回好项目的方法,以及一个仅返回坏项目的方法。

class Item(models.Model):
objects = ItemQuerySet.as_manager()
name = models.CharField(max_length=255, unique=True)

class List(models.Model):
name = models.CharField(max_length=255, unique=True)
bad = models.BooleanField(default=True)
items = models.ManyToManyField(Item, related_name='lists')

class ItemQuerySet(models.QuerySet):
def bad(self):
return self.exclude(lists__bad=False)

def good(self):
return self.filter(lists__bad=False)

场景

以下是我们遇到问题的场景示例:一个坏列表、一个好列表和两个项目。

BadList:    GoodList:
- Item1 - Item1
- Item2

由于 Item1 至少出现在一个好列表中,因此它应该出现在 Item.objects.good() 中,而不是出现在 Item.objects.bad() 中.

由于 Item2 没有出现在任何好的列表中,因此它应该出现在 Item.objects.bad() 中,而不是出现在 Item.objects.good() 中.

我们可以像这样设置场景:

# Create the two lists.
>>> goodlist = List.objects.create(name='goodlist', bad=False)
>>> badlist = List.objects.create(name='badlist', bad=True)

# Create the two items.
>>> item1 = Item.objects.create(name='item1')
>>> item2 = Item.objects.create(name='item2')

# Item1 goes in both lists
>>> goodlist.items.add(item1)
>>> badlist.items.add(item1)

# Item2 only in badlist
>>> badlist.items.add(item2)

事实上,Item.objects.good()Item.objects.bad() 按我们的预期工作:

>>> Item.objects.bad() # This returns what we want! Good!
<QuerySet [<Item: item2>]>

>>> Item.objects.good() # This returns what we want! Good!
<QuerySet [<Item: item1>]>

问题

谢谢你对我的包容。这就是我们的自定义查询集出错的地方。如果我们通过单个列表的 Items 访问 good()bad() 自定义 QuerySet 方法,我们会得到不正确的结果。

>>> badlist.items.bad() # WRONG! We want to ONLY see item2 here!
<QuerySet [<Item: item1>, <Item: item2>]

>>> badlist.items.good() # WRONG! We want to see item1 here!
<QuerySet []>

看起来,当我们执行 badlist.items.bad() 时,查询考虑 badlist 来确定 Items 是否为不好,而不是考虑项目所在的所有列表。但我很困惑为什么会出现这种情况。

我的想法是,在 ItemQuerySet.bad 方法中,我想要类似 self.exclude(any__lists__bad=False) 的东西,而不仅仅是 self.exclude (lists__bad=False)。但当然, any__ 关键字实际上并不存在,而且我不确定如何在 Django QuerySet 中正确表达该逻辑。看起来使用 Q 对象可能是前进的方向,但我仍然不太确定如何使用 Q 对象表达这样的查询。

在我们的实际数据库中,只有不到 100 个 List,但有数百万个 Item。因此,出于性能原因,最好使用一个查询来完成此操作,而不是使用一个属性或多个查询。

干杯!

最佳答案

如果打印出由 badlist.items.bad() 生成的查询,您会看到问题:它将在 through 表上使用 WHERE 子句,从而将列表限制为仅不良列表。如果您想正确应用badgood,您需要从Item级别开始,然后按列表中的项目进行过滤。

item_ids = list(badlist.items.values_list('id'), flat=True)

Item.objects.bad().filter(id__in=item_ids)

Item.objects.good().filter(id__in=item_ids)

编辑:如果没有架构,我无法对此进行测试,但我认为您可以使用注释来计算列表的数量,然后通过该过滤

def annotate_good(self);
return self.annotate(good=Count(Case(When(lists__bad=False, then=1), default=0)))

def good(self):
return self.annotate_good().exclude(good=0)

def bad(self):
return self.annotate_good().filter(good=0)

否则,如果性能确实是一个问题,我会向 Item 模型添加一个好的或坏的字段,并在保存时更新它,以便查询变得非常简单。

关于python - 根据ManyToMany关系存在过滤Django QuerySet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44528734/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com