web-dev-qa-db-ja.com

Django異なるカテゴリから最新のオブジェクトを取得するクエリ

2つのモデルABがあります。すべてのBオブジェクトには、Aオブジェクトへの外部キーがあります。 Aオブジェクトのセットが与えられたとしても、ORMを使用して、各Bオブジェクトに対して作成された最新のオブジェクトを含むAオブジェクトのセットを取得する方法はありますか。

簡単な例を次に示します。

class Bakery(models.Model):
    town = models.CharField(max_length=255)

class Cake(models.Model):
    bakery = models.ForeignKey(Bakery, on_delete=models.CASCADE)
    baked_at = models.DateTimeField()

そこで、アメリカのエニタウンにある各パン屋で焼いた最新のケーキを返すクエリを探しています。

73
Zach

私の知る限り、Django ORMでこれを行うための1ステップの方法はありません。

ただし、次の2つのクエリに分割できます。

bakeries = Bakery.objects.annotate(
    hottest_cake_baked_at=Max('cake__baked_at')
) 
hottest_cakes = Cake.objects.filter(
    baked_at__in=[b.hottest_cake_baked_at for b in bakeries]
)

ケーキのIDがbake_atタイムスタンプとともに進行している場合は、上記のコードを簡略化して明確にすることができます(2つのケーキが同時に到着した場合、両方を取得できます)。

hottest_cake_ids = Bakery.objects.annotate(
    hottest_cake_id=Max('cake__id')
).values_list('hottest_cak‌​e_id', flat=True)

hottest_cakes = Cake.objects.filter(id__in=hottest_cake_ids)

BTWの功績はダニエルローズマン氏にあります。ダニエルローズマン氏はかつて私の同様の質問に答えました。

http://groups.google.pl/group/Django-users/browse_thread/thread/3b3cd4cbad478d34/3e4c87f336696054?hl=pl&q=

上記の方法が遅すぎる場合は、2番目の方法も知っています。関連するベーカリーで最もホットなケーキのみを生成するカスタムSQLを記述し、それをデータベースVIEWとして定義してから、管理されていないDjangoモデルを書き込むことができます。 。上記のDjango-usersスレッドでも言及されています。元のコンセプトへの直接リンクはここにあります:

http://web.archive.org/web/20130203180037/http://wolfram.kriesing.de/blog/index.php/2007/Django-Nice-and-critical-article#comment-48425

お役に立てれば。

29

から始まる Django 1.11そして SubqueryOuterRef のおかげで、ようやくlatest-per-groupORMを使用したクエリ。

hottest_cakes = Cake.objects.filter(
    baked_at=Subquery(
        (Cake.objects
            .filter(bakery=OuterRef('bakery'))
            .values('bakery')
            .annotate(last_bake=Max('baked_at'))
            .values('last_bake')[:1]
        )
    )
)

#BONUS, we can now use this for prefetch_related()
bakeries = Bakery.objects.all().prefetch_related(
    Prefetch('cake_set',
        queryset=hottest_cakes,
        to_attr='hottest_cakes'
    )
)

#usage
for bakery in bakeries:
    print 'Bakery %s has %s hottest_cakes' % (bakery, len(bakery.hottest_cakes))
21
Todor

たまたまPostGreSQLを使用している場合は、 DjangoのDISTINCT ONへのインターフェイス を使用できます。

recent_cakes = Cake.objects.order_by('bakery__id', '-baked_at').distinct('bakery__id')

ドキュメント と言うように、order byと同じフィールドdistinct on。 Simonが以下で指摘しているように、追加のソートを実行したい場合は、Pythonスペースで行う必要があります。

18
dbn

これは仕事をするはずです:

from Django.db.models import Max
Bakery.objects.annotate(Max('cake__baked_at'))
5
Daniel Roseman

私は同様の問題を抱えて戦い、ついに次の解決策にたどり着きました。 order_bydistinctに依存しないため、db側で必要に応じて並べ替えることができ、フィルタリング用のネストされたクエリとして使用することもできます。また、この実装は標準のsql HAVING句に基づいているため、dbエンジンに依存しないと考えています。唯一の欠点は、ベーカリーで正確に同時に焼いた場合、ベーカリーごとに最もホットなケーキが複数返されることです。

from Django.db.models import Max, F

Cake.objects.annotate(
    # annotate with MAX "baked_at" over all cakes in bakery
    latest_baketime_in_bakery=Max('bakery__cake_set__baked_at')
    # compare this cake "baked_at" with annotated latest in bakery
).filter(latest_baketime_in_bakery__eq=F('baked_at'))
3
Ivan Klass
_Cake.objects.filter(bakery__town="Anytown").order_by("-created_at")[:1]
_

私は自分の側でモデルを構築していませんが、理論的にはこれでうまくいくはずです。故障:

  • Cake.objects.filter(bakery__town="Anytown")国が文字列の一部ではない場合、「Anytown」に属するケーキを返す必要があります。 bakerytownの間の二重下線により、townbakeryプロパティにアクセスできます。
  • .order_by("-created_at")は、作成日順に新しい結果を並べ替えます(_-_の_"-created_at"_(マイナス)記号に注意してください。マイナス記号がない場合、それらは順序付けられます。古いものから新しいものへ。
  • 最後の_[:1]_は、返されるリストの最初の項目のみを返します(これは、Anytownからのケーキのリストであり、最新のものから順に並べられます)。

注:この回答はDjango 1.11向けです。この回答は、Djangoに表示されているクエリ から変更されています] 1.11ドキュメント

0
twknab