Django:根据最新的子模型字段排序查询集

Django:根据最新的子模型字段排序查询集,django,django-models,django-queryset,Django,Django Models,Django Queryset,让我们假设我想显示一个按最新冲刺时间排序的跑步者列表 class Runner(models.Model): name = models.CharField(max_length=255) class Sprint(models.Model): runner = models.ForeignKey(Runner) time = models.PositiveIntegerField() created = models.DateTimeField(auto_no

让我们假设我想显示一个按最新冲刺时间排序的跑步者列表

class Runner(models.Model):
    name = models.CharField(max_length=255)

class Sprint(models.Model):
    runner = models.ForeignKey(Runner)
    time = models.PositiveIntegerField()
    created = models.DateTimeField(auto_now_add=True)
这是我将在SQL中执行的操作的简要说明:

SELECT runner.id, runner.name, sprint.time
FROM runner
LEFT JOIN sprint ON (sprint.runner_id = runner.id)
WHERE 
  sprint.id = (
    SELECT sprint_inner.id
    FROM sprint as sprint_inner
    WHERE sprint_inner.runner_id = runner.id
    ORDER BY sprint_inner.created DESC
    LIMIT 1
  )
  OR sprint.id = NULL
ORDER BY sprint.time ASC
各国:

允许指定多值字段对结果进行排序 by(例如,ManyToManyField字段)。通常情况下,这不会是一场灾难 这是一个明智的做法,它确实是一个高级使用功能。 但是,如果您知道查询集的筛选或可用数据 这意味着对于每一个用户,只有一个排序数据块 您所选择的主要商品,订购顺序可能完全相同 你想做什么。在多值字段上使用排序时要谨慎且 确保结果符合您的预期

我想我需要在这里应用一些过滤器,但我不确定Django到底期望什么

需要注意的是,在本例中并不明显:Runner表将有几百个条目,Sprint表也将有几百个条目,在以后的几天中可能会有几千个条目。数据将显示在分页视图中,因此Python中的排序不是一个选项


我看到的唯一的另一种可能性是自己编写SQL,但我想不惜一切代价避免这种情况。

我不认为只有一个查询就可以通过ORM实现这一点,你可以获取一个跑步者列表并使用它添加他们最新的冲刺id,然后过滤和排序这些冲刺

def view_name(request):
    spr = Sprint.objects.values('runner', flat=True).order_by(-created).distinct()
    runners = []
    for s in spr:
        latest_sprint = Sprint.objects.filter(runner=s.runner).order_by(-created)[:1]
        for latest in latest_sprint:
            runners.append({'runner': s.runner, 'time': latest.time})

    return render(request, 'page.html', {
            'runners': runners,
    })


{% for runner in runners %}
    {{runner.runner}} - {{runner.time}}
{% endfor %}
>>> from django.db.models import Max

# all runners now have a `last_race` attribute,
# which is the `id` of the last sprint they ran
>>> runners = Runner.objects.annotate(last_race=Max("sprint__id"))

# a list of each runner's last sprint ordered by the the sprint's time,
# we use `select_related` to limit lookup queries later on
>>> results = Sprint.objects.filter(id__in=[runner.last_race for runner in runners])
...                         .order_by("time")
...                         .select_related("runner")

# grab the first result
>>> first_result = results[0]

# you can access the runner's details via `.runner`, e.g. `first_result.runner.name`
>>> isinstance(first_result.runner, Runner)
True

# this should only ever execute 2 queries, no matter what you do with the results
>>> from django.db import connection
>>> len(connection.queries)
2
这相当快,并且仍将利用数据库的索引和缓存


几千条记录并不是那么多,这对于那些类型的数字应该很有效。如果您开始遇到问题,我建议您咬紧牙关,使用原始SQL。

问题不是获取最新的sprint,而是按照最新的sprint
时间
字段排序Runner QuerySet。这确实有效,是的。问题是,这会将运行程序的顺序转移到应用程序中,这至少会导致大量内存使用和相对较高的CPU使用。请参阅有关表格大小的更新问题。这种方法的另一个问题是,它不会显示任何没有冲刺的跑步者。虽然这也可以在python代码中解决,但这对于数据库来说是一项完美的工作,因为它可以利用其索引和缓存。这适用于小型数据库,但如果我这样做,我们的系统管理员会杀了我;)嗯……这很难。我们都是一样的,出于雇主的期望,我对自己的工作非常谨慎这不会导致相对较高的内存使用率吗?据我所知,它至少会将每个跑步者拉入内存,并建立一个相当大的sprint ID列表。在DB中有几百名跑步者的每个页面视图上这样做让我感到有点不舒服。我想这就是缓存的作用所在。在使用10000个运行程序进行测试后,它使用了不到10MB(实际上是3MB…)的RAM。如果您认为您需要的不仅仅是这些,那么您真的应该使用原始SQL。和往常一样,解决这一问题的最佳方法是先分析——而不是猜测。过早的优化和所有这些…而且,几百条记录真的不是很多…当然不足以保证担心性能优化。几十万条记录通常是您开始考虑它的地方,即使这样,它通常也不是什么大问题(加上一两个索引,它就解决了)。感谢您的测试!我想这是解决这个问题的合理办法。可悲的是,经过一段时间的思考,我意识到这并不能解决我的确切用例中的问题。首先,我需要按两个多个字段进行排序,其次,这不会显示根本没有冲刺的跑步者。我想我只能自己写SQL了:/