让我们假设我想显示按其最新冲刺时间排序的跑步者名单。
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
Django QuerySet documentation州:
允许指定多值字段以对结果进行排序 通过(例如,ManyToManyField字段)。通常这不会是一个 明智的做法,它真的是一个先进的使用功能。 但是,如果您知道您的queryset的过滤或可用数据 意味着每个只有一个订购数据 您选择的主要项目,订购可能是完全正确的 你想做什么。谨慎使用多值字段的排序 确保结果符合您的预期。
我想我需要在这里应用一些过滤器,但我不确定Django究竟期待什么......
一个注意事项,因为在这个例子中并不明显:Runner表将有几百个条目,sprint也将有几百个,在稍后的几天可能有几千个条目。数据将以分页视图显示,因此无法在Python中进行排序。
我看到的另一个可能性就是自己编写SQL,但我想不惜一切代价避免这种情况。
答案 0 :(得分:2)
我认为通过只有一个查询的ORM有办法实现这一点,您可以获取一个跑步者列表并使用annotate添加他们最新的sprint id - 然后过滤并订购这些sprint
>>> 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。
答案 1 :(得分:0)
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 %}