ManyToManyField对象上的Django可变数量的过滤器?

Posted

技术标签:

【中文标题】ManyToManyField对象上的Django可变数量的过滤器?【英文标题】:Django variable number of filters on ManyToManyField objects? 【发布时间】:2021-04-19 14:49:17 【问题描述】:

我想使用 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()

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

感谢您的帮助!

【问题讨论】:

可以动态生成一些Q对象。检查这个答案:***.com/a/13076894/4151233 是的,但我很难为我的案例做出正确的 Q 表达式。我最后给出的那个渲染空集,而带有级联过滤器的那个可以正常工作。 【参考方案1】:

您想要或 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()

【讨论】:

但我认为这意味着我搜索将 >=rating1 给 movie1 或 >=rating2 给 movie2 的用户,但我想搜索同时评论了 Movie1 和 Movie2 并分别在上面给出的用户rating1 和 rating2。 在这种情况下,您使用级联过滤器的方法是我能想到的唯一方法。即使我们只考虑 SQL,我能想到的唯一方法就是对评级表进行多个连接。另外,为什么要在 Person 中设置多对多字段,更好的方法是将 Person 的外键放在 Rating 中。 是的,您对 fereign 密钥的看法是正确的。我想我需要重新考虑模型,因为我概述的搜索案例是我的主要目标......

以上是关于ManyToManyField对象上的Django可变数量的过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

Django:ManyToManyField,如果对象不存在则添加它

删除 django 模型中不相关的对象(manytomanyfield)

Django 计数嵌套的 ManyToManyField 对象

Django:在ManyToManyField中获取属于模型对象的第一个项目

如何在 Django RestFramework API 中将 manytomanyfield 作为对象返回

Django 上的用户追随者模型。不能在指定中间模型的 ManyToManyField 上使用 add()。改用accounts.Contact的经理