简体   繁体   English

筛选查询集以仅返回每个用户的最佳结果

[英]Filter queryset to return only the best result for each user

I have a couple of django models, one of which holds a number of user results for different events. 我有几个django模型,其中一个模型可以容纳许多不同事件的用户结果。 I'm looking for a way to generate a queryset consisting of only the best (highest) result for each user that also has the other attributes attached (like the date of the result). 我正在寻找一种生成查询集的方法,该查询集仅由每个用户的最佳(最高)结果组成,并且还附加了其他属性(例如结果的日期)。

My models are as shown as well as using the built in user model: 我的模型如图所示,并使用内置的用户模型:

class CombineEvents(models.Model):
    team = models.ForeignKey(Team)
    event = models.CharField(max_length=100)
    metric = models.CharField(max_length=100)
    lead_order = models.IntegerField()

    def __unicode__(self):
        return self.event

class CombineResults(models.Model):
    user = models.ForeignKey(User)
    date = models.DateField()
    event = models.ForeignKey(CombineEvents)
    result = models.FloatField()

    def __unicode__(self):
        return str(self.date) + " " + str(self.event)

I am iterating through each event and attaching a queryset of the events results, which is working fine, but I want that sub-queryset to only include one object for each user and that object should be that user's best result. 我正在遍历每个事件并附加事件结果的查询集,效果很好,但是我希望该子查询集仅为每个用户包括一个对象,并且该对象应该是该用户的最佳结果。 My queryset code is below: 我的查询集代码如下:

    combine_events = CombineEvents.objects.filter(team__id=team_id)
    for event in combine_events:
        event.results = CombineResults.objects.filter(event=event)

I'm not sure how filter down to just those best results for each user. 我不确定如何过滤每个用户的最佳结果。 I want to use these querysets to create leaderboards, so I'd still like to be able to also have the date of that best result and the user name, but don't want the leaderboard to allow more than one spot per user. 我想使用这些查询集来创建排行榜,因此我仍然希望能够获得最佳结果的日期和用户名,但是不希望排行榜为每个用户提供一个以上的位置。 Any ideas? 有任何想法吗?

Since your CombineResults model has a FK relation to CombineEvents , you can do something like this: 由于您的CombineResults模型与CombineResults具有FK关系, CombineEvents您可以执行以下操作:

combine_events = CombineEvents.objects.filter(team__id=team_id)
for event in combine_events:
    result = event.combineresults_set.order_by('-result')[0]

The combineresults_set attribute is auto-generated by the FK field, though you can set it to something more helpful by specifying the related_name keyword argument: 尽管可以通过指定related_name关键字参数将其设置为更有用的方法,但是FK字段会自动生成combineresults_set属性:

class CombineResults(models.Model):
    event = models.ForeignKey(CombineEvents, related_name='results')

would enable you to call event.results.order_by(...) . 将使您能够调用event.results.order_by(...) There is more in the documentation here: https://docs.djangoproject.com/en/1.9/topics/db/queries/#following-relationships-backward 文档中还有更多内容: https : //docs.djangoproject.com/en/1.9/topics/db/queries/#following-relationships-backward

Note that this isn't the most DB-friendly approach as you will effectively hit the database once to get combine_events (as soon you start iterating), and then again for each event in that list. 请注意,这不是对数据库最友好的方法,因为您将有效地访问数据库一次以获取combine_events (一旦您开始迭代),然后再次访问该列表中的每个事件。 It will probably be better to use prefetch_related() , which you can use to make two DB queries only. 最好使用prefetch_related() ,您可以仅使用它进行两个数据库查询。 Documentation can be found here . 文档可以在这里找到

prefetch_related() however will default to do a queryset.all() for the related documents, which you could further control by using Prefetch objects as documented here . 但是prefetch_related()将默认对相关文档执行queryset.all() ,您可以通过使用此处记录的 Prefetch对象来进一步控制。

Edit: Apologies for getting the question wrong. 编辑:道歉为错误的问题。 Getting every user's best result per event (which is what I think you want) is not quite as simple. 在每个事件中获得每个用户的最佳结果 (这是我认为您想要的)并不是那么简单。 I'd probably do something like this: 我可能会做这样的事情:

from django.db.models import Q, Max
combine_events = CombineEvents.objects \
    .filter(team_id=team_id) \
    .prefetch_related('combineresults_set')

for event in combine_events:
    # Get the value of the best result per user
    result = event.combineresults_set.values('user').annotate(best=Max('result'))

    # Now construct a Q() object, note this will evaluate the result query
    base_q = Q()
    for res in result:
        # this is the equivalent to base_q = base_q | ....
        base_q |= (Q(user_id=res['user']) & Q(result=res['best']))

    # Now you're ready to filter results
    result = event.combineresults_set.filter(base_q)

You can read more about Q objects here , or alternatively write your own SQL using RawSQL and the likes. 您可以在此处阅读有关Q对象的更多信息,或者使用RawSQL等类似方法编写自己的SQL。 Or wait for someone with a better idea.. 或者等待有更好主意的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM