9

最新のスプリント時間順に並べたランナーのリストを表示したいとします。

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のドキュメントには次のように記載されています。

結果を並べ替えるには、多値フィールドを指定することができます (たとえば、ManyToManyField フィールド)。通常、これは賢明なことではなく、実際には高度な使用機能です。ただし、クエリセットのフィルタリングまたは利用可能なデータが、選択している主要な項目ごとに 1 つの順序付けデータしかないことを意味することがわかっている場合、順序付けはまさにあなたがやりたいことである可能性があります。複数値フィールドの順序付けは注意して使用し、結果が期待どおりであることを確認してください。

ここで何らかのフィルターを適用する必要があると思いますが、Django が正確に何を期待しているのかわかりません...

この例では明らかではないため、1 つ注意してください。ランナー テーブルには数百のエントリがあり、スプリントにも数百のエントリがあり、後でおそらく数千のエントリがあります。データはページ分割されたビューで表示されるため、Python での並べ替えはオプションではありません。

私が目にする唯一の他の可能性は、自分で SQL を書くことですが、これはどうしても避けたいと思います。

4

2 に答える 2

4

クエリを1つだけ使用してORMを介してこれを行う方法はないと思います。ランナーのリストを取得し、注釈を使用して最新のスプリントIDを追加し、それらのスプリントをフィルタリングして並べ替えることができます。

>>> 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を使用することをお勧めします。

于 2013-03-02T02:13:45.367 に答える
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 %}
于 2013-03-01T10:12:45.943 に答える