我有一个m2m字段users
的模型:
class SomeModel(models.Model):
objects = SomeModelManager()
users = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
我的目标是得到这个模型的示例,其中示例用户集匹配给定的查询集(这意味着查询集中的每个用户都应该是m2m关系,没有其他用户)。
如果我做了
obj = SomeModel.objects.get(users=qs)
我明白
ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.
我完全理解这样的错误的原因,所以我做的下一件事是为这个模型创建一个自定义的Queryset类来覆盖.get()
行为:
class SomeModelQueryset(QuerySet):
def get(self, *args, **kwargs):
qs = super() # Prevent recursion
if (users := kwargs.pop('users', None)) is not None:
qs = qs.annotate(count=Count('users__id')).filter(users__in=users, count=users.count()**2)
return qs.get(*args, **kwargs)
class SomeModelManager(models.Manager.from_queryset(SomeModelQueryset)):
...
因此,我尝试只过滤具有匹配用户的对象,并确保用户数量与queryset中的相同。
但我不喜欢当前版本的代码。users__in
在每次找到匹配时都会将示例添加到查询集,因此它会导致每个对象出现n
次(n
-特定对象的m2m用户数)。.annotate()
中的Count
对每次出现的唯一用户ID进行计数,然后将所有计数合并生成单个对象。因此,对于每个对象,都有n
次出现,计数为n
,结果对象的计数为n**2
。
有没有办法重写这个annotate+filter来生成count=n,而不是n^2?
1条答案
按热度按时间gopyfrb31#
您可以使用
__in
过滤器进行检查,然后确定用户数的计数: