首页 > 解决方案 > ManyToManyField对象上的Django可变数量的过滤器?

问题描述

我想使用 ManyToManyFeild 指向的对象的两个属性来过滤 ManyToManyField 上的对象。看下面的模型,这是一个经典的用户-项目关系

from django.db import models

class Person(models.Model):
#    user = models.OneToOneField(User)
    name = models.CharField(max_length=50, blank=False, null=False, default=None, unique=True)
    ratings = models.ManyToManyField('Rating',
        related_name="persons"
    )


class Movie(models.Model):
    title = models.CharField(max_length=50, blank=False, null=False, default=None, unique=True)

    def __str__(self):
        return f"{self.title}"

class Rating(models.Model):
    movie = models.ForeignKey(Movie, models.CASCADE)
    value = models.SmallIntegerField(blank=False, null=False, default=None)

    def __str__(self):
        return f"{self.movie}:{self.value}"

所以,我想找到所有对 Movie1 给予高于特定评级和对 Movie2 给予高于特定评级的用户。

我可以用级联过滤器做到这一点:

>>> Person.objects\
 .filter(ratings__movie__title='Movie1', ratings__value__gte=5)\
 .filter(ratings__movie__title='Movie2', ratings__value__gte=1)\
 .all()
[[A<QuerySet [<Person: Person object (1)>]>

但我正在寻找一种方法来过滤任意数量的评级,而不仅仅是 2。

我尝试过使用 Q (在filter()所有情况下都是单一的),但以下方法不起作用:

>>> Person.objects.filter(
  Q(ratings__movie__title='Movie1') & Q(ratings__value__gte=5),\
  Q(ratings__movie__title='Movie2') & Q(ratings__value__gte=1)
).all()
<QuerySet []>

更新:

我发现,由于过滤器是惰性的,我可以在一个循环中级联所需数量的过滤器,然后.all()在最后调用,如下所示:

my_filters = [("Movie1", 2), ("Movie2", 3)]
f = Person.objects
for m,r in my_filters:
  f = f.filter(ratings__movie__title=m, ratings__value__gte=r)

selected_persons = f.all()

当然,问题仍然存在,该查询的效率如何......所以问题仍然是如何以最佳效率执行此操作。

感谢帮助!

标签: djangodjango-modelsdjango-queryset

解决方案


您想要或 Q 对象您当前的尝试和它们,这基本上意味着电影标题必须是 Movie1 AND Movie2 并且评级必须是 5 AND 1。
这可能会如您所愿:-

Person.objects.filter(
  (Q(ratings__movie__title='Movie1') & Q(ratings__value__gte=5)) | (Q(ratings__movie__title='Movie2') & Q(ratings__value__gte=1))
).all()

推荐阅读