我在减少特定视图的查询数量方面遇到了麻烦。这是一个相当沉重的,但我相信它可以减少:
Profile:
name = CharField()
Officers:
club= ManyToManyField(Club, related_name='officers')
title= CharField()
Club:
name = CharField()
members = ManyToManyField(Profile)
Election:
club = ForeignKey(Club)
elected = ForeignKey(Profile)
title= CharField()
when = DateTimeField()
俱乐部有会员和官员(总裁,比赛总监)。人们可以成为多个俱乐部的成员...... 在选举中选举官员,其结果将被存储。
鉴于一名球员,我怎样才能找到每个球员俱乐部最近当选的官员?
目前我有
clubs = Club.objects.filter(members=me).prefetch_related('officers')
for c in clubs:
officers = c.officers.all()
most_recent = Elections.objects.filter(club=c).filter(elected__in=officers).order_by('-when')[:1].get()
print(c.name + ' elected ' + most_recent.name + ' most recently')
问题是循环查询,如果你是1个俱乐部的成员,那么它很好而且速度很快但是如果你加入了我的数据库,我会加入数据库。
修改 Nil的答案是我想做的,但没有得到对象。我真的不需要这个对象,但我确实需要另一个字段以及日期时间。如果查询有用:
Club.objects.annotate(last_election=Max('election__when'))
生成原始SQL
SELECT "organisation_club"."id", "organisation_club"."name", MAX("organisation_election"."when") AS "last_election"
FROM "organisation_club"
LEFT OUTER JOIN "organisation_election" ON ( "organisation_club"."id" = "organisation_election"."club_id" )
GROUP BY "organisation_club"."id", "organisation_club"."name"
如果可能的话(或者主要是'ORM答案),我真的很喜欢ORM答案。
答案 0 :(得分:5)
我相信这是你正在寻找的:
from django.db.models import Max, F
Election.objects.filter(club__members=me) \
.annotate(max_date=Max('club__election_set__when')) \
.filter(when=F('max_date')).select_related('elected')
可以在一个声明中再次关注前后关系,允许您为与当前选举俱乐部相关的任何选举注释max_date。 F
类允许您根据SQL中的选定字段过滤查询集,包括通过注释,聚合,连接等添加的任何额外字段。
答案 1 :(得分:3)
您希望在SQL术语中定义here:查询Election
表,按Club
对它们进行分组,并仅保留每个俱乐部的最后一次选举。
现在,我们如何在Django ORM中翻译它?查看documentation,我们了解到我们可以使用注释来完成它。诀窍是你需要反过来思考。您希望在每个俱乐部的最后一次选举中注释(添加新数据)。这给了我们:
Club.objects.annotate(last_election=Max('election__when'))
# Use it in a for loop like that
for club in Club.objects.annotate(last_election=Max('election__when')):
print(club, club.last_election)
可悲的是,这只会添加日期,但不会回答您的问题!您需要名称或完整的Club
对象。我搜查了,我仍然不知道如何正确地做到这一点。如果一切都失败了,你仍然可以使用像第一个链接中的查询在Django中使用raw SQL query。
答案 2 :(得分:1)
我能想到的最简单的方法是在应用程序级别部分过滤
如果你这样做
e = Election.objects.filter(club__members=me).select_related('elected')
或
e = me.club_set.election_set.select_related('elected')
这是一个单一的查询,它应该收回会员me
所在的所有俱乐部所发生的所有选举。然后你可以使用python来获取最新的日期。当然,如果每个俱乐部有很多选举,你最终会获取的数据远远超过使用的数据。
应该在两个查询中执行此操作的另一种方法:
# Get all member's clubs & most recent election
clubs = Club.objects.filter(members=me).annotate(last_election=Max('election__when'))
# Create filters for election based on the club id and the latest election time
election_Q = [Q(club__id=c.id) & Q(when=c.last_election) for c in clubs]
# Combine filters with an OR
election_filter = reduce(lambda f1, f2: f1 | f2, election_Q)
# Get elections restricting by specific clubs & election date
elections = Election.objects.filter(election_filter).select_related('elected')
for e in elections:
print '%s elected %s most recently at %s' % (e.club.name, e.elected, e.when)
这是基于@Nil的方法构建的,并使用其结果在python中构建查询,然后将其提供给第二个查询。但是,SQL语句的大小存在限制,如果成员所在的俱乐部很多,那么您可能会达到限制。虽然限制相当高,但我在单个INSERT语句中导入大型数据集时只能达到它,所以我认为它应该适用于您的目的。
很抱歉,我无法想到Django ORM可以使用单个SQL查询将它们链接在一起的方式。对于复杂的查询,Django ORM实际上是quite limited所以如果你真的需要效率,我认为编写原始SQL查询可能是最好的。