2012-01-06 118 views
1

假设我有以下型号:添加额外的多对多领域Django的查询集

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()都不会帮助我。

回答

0

由于您打算在一页中显示所有内容。我可以想到这种方法。您可以试试这个:

获取当前用户提供的所有评级并获取所有内容。

现在尝试创建一个字典是这样的:

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包含模型事情的所有条目作为键,并具有其评级作为其值。

可能不是最好的方法。我很想看到别人的回答。

+0

您仍然得到每个用户的查询,而不是聚合。 – Tom 2012-01-06 17:33:58

+0

好的,但它绝对不是完美的。如果事情分成页面怎么办?比如说,有10000个东西,页面包含100个条目。第二个循环将不得不查看所有可能的10000个评分。 – user285176 2012-01-06 19:03:10

+0

@Tom至少有2个查询,而不是n + 1 – user285176 2012-01-06 19:05:18

3

我想你应该试试这个: 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个查询。

希望这会有所帮助:)

+0

看起来好多了,谢谢。它会执行嵌套的SELECT,我理解正确吗?所以基本上数据库执行相同数量的工作,并且JOIN仍然是可取的。 – user285176 2012-01-07 07:42:39

+1

你可以通过打印connection.queries忽略查询,还有另一种方法,你可以写一个自定义的原始sql查询(使用连接。光标) – 2012-01-07 09:43:32

+0

作业的数量是不同的,一个事务,一个数据库连接,一个嵌套查询(而不是n + 1个查询),它的速度相对较快 – 2012-01-07 09:50:14