在 django 中向 QuerySet 添加 ManyToMany 额外字段
Posted
技术标签:
【中文标题】在 django 中向 QuerySet 添加 ManyToMany 额外字段【英文标题】:Adding ManyToMany extra field to QuerySet in django 【发布时间】:2012-02-04 09:29:22 【问题描述】:假设我有以下模型:
class Thing(models.Model):
name = models.CharField(max_length=100)
ratings = models.ManyToManyField('auth.User', through='Rating')
class Rating(models.Model):
user = models.ForeignKey('auth.User')
thing = models.ForeignKey('Thing')
rating = models.IntegerField()
所以我有很多东西,每个用户都可以评价每件事。我还有一个视图显示所有事物的列表(它们的数量很大)以及用户分配给每个事物的评级。我需要一种从数据库中检索所有数据的方法:具有附加字段 user_rating
的事物对象最多取自一个(因为我们有一个固定的用户)相关的评级对象。
简单的解决方案如下所示:
things = Thing.objects.all()
for thing in things:
try:
thing.user_rating = thing.ratings.objects.get(user=request.user).rating
except Rating.DoesNotExist:
thing.user_rating = None
但是这种方法的缺陷很明显:如果我们有 500 个东西,我们将对数据库发出 501 个请求。每页。每个用户。这是该网站浏览次数最多的页面。这个任务很容易用 SQL JOIN 解决,但实际上我有更复杂的模式,我肯定会从 Django 模型框架中受益。所以问题是:有可能以 Django 方式进行吗?考虑到这样的任务很常见,如果不是这样就真的很奇怪了。
据我了解,annotate()
和 select_related()
都不会在这里帮助我。
【问题讨论】:
【参考方案1】:因为您打算在一页中显示所有内容。我可以想到这种方法。你可以试试这个:
获取当前用户给出的所有评分并获取所有事物。
现在尝试像这样创建一个字典:
thing_dict =
for thing in Thing.objects.all():
thing_dict[thing] = None
for rating in Rating.objects.filter(user = request.user):
thing_dict[rating.thing] = rating
现在 thing_dict 包含模型 Thing 的所有条目作为键,并将其评级作为其值。
可能不是最好的方法。我很想看看其他人的回答。
【讨论】:
您仍然得到每个用户的查询而不是聚合。 好的,但它肯定不是完美的。如果事情被分成几页怎么办?比如说,有 10000 个东西,页面包含 100 个条目。第二个循环必须查看所有可能的 10000 个评分。 @Tom 至少有 2 个查询而不是 n+1 @user285176,如果我们将其拆分为多个页面,这肯定是个坏主意。根据用户对事物评分的数量,我们可以考虑以非规范化的方式存储评分信息。【参考方案2】:我想你应该试试这个: https://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra
例子
result = Thing.objects.all().extra(select='rating': 'select rating from ratings where thing_id = id')
您的结果集为每个“事物”对象获取一个新字段“评级”。
我在最近的一个项目中使用了这种方法。它产生一个复杂的查询而不是 n+1 个查询。
希望这会有所帮助:)
【讨论】:
看起来好多了,谢谢。它将执行嵌套 SELECT,我理解正确吗?所以基本上数据库执行相同数量的工作,JOIN 仍然是可取的。 您可以通过打印connection.queries来忽略查询,还有另一种方法,您可以编写一个自定义的原始sql查询(使用connection.cursor) 作业量不同,一个事务,一个数据库连接,一个嵌套查询(而不是n+1个查询),比较快 F 表达式不是最安全的方式吗?见docs.djangoproject.com/en/dev/ref/models/expressions/… @radtek 自从我提出解决方案以来已经三年多了。如果我得到正确的文档,F 表达式被添加到 django 1.8(5 个版本以后)中。如果有一个现代解决方案可以产生相同的结果并且更优雅,那么非常欢迎您创建一个替代答案。以上是关于在 django 中向 QuerySet 添加 ManyToMany 额外字段的主要内容,如果未能解决你的问题,请参考以下文章
在django中动态添加参数到queryset过滤器调用[重复]
如何在 Django 中向 ManyToManyField 添加条目?