2つのモデルA
とB
があります。すべての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()
そこで、アメリカのエニタウンにある各パン屋で焼いた最新のケーキを返すクエリを探しています。
私の知る限り、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_cake_id', flat=True)
hottest_cakes = Cake.objects.filter(id__in=hottest_cake_ids)
BTWの功績はダニエルローズマン氏にあります。ダニエルローズマン氏はかつて私の同様の質問に答えました。
上記の方法が遅すぎる場合は、2番目の方法も知っています。関連するベーカリーで最もホットなケーキのみを生成するカスタムSQLを記述し、それをデータベースVIEWとして定義してから、管理されていないDjangoモデルを書き込むことができます。 。上記のDjango-usersスレッドでも言及されています。元のコンセプトへの直接リンクはここにあります:
お役に立てれば。
から始まる Django 1.11
そして Subquery と OuterRef のおかげで、ようやくlatest-per-group
ORM
を使用したクエリ。
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))
たまたまPostGreSQLを使用している場合は、 DjangoのDISTINCT ONへのインターフェイス を使用できます。
recent_cakes = Cake.objects.order_by('bakery__id', '-baked_at').distinct('bakery__id')
ドキュメント と言うように、order by
と同じフィールドdistinct on
。 Simonが以下で指摘しているように、追加のソートを実行したい場合は、Pythonスペースで行う必要があります。
これは仕事をするはずです:
from Django.db.models import Max
Bakery.objects.annotate(Max('cake__baked_at'))
私は同様の問題を抱えて戦い、ついに次の解決策にたどり着きました。 order_by
とdistinct
に依存しないため、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'))
_Cake.objects.filter(bakery__town="Anytown").order_by("-created_at")[:1]
_
私は自分の側でモデルを構築していませんが、理論的にはこれでうまくいくはずです。故障:
Cake.objects.filter(bakery__town="Anytown")
国が文字列の一部ではない場合、「Anytown」に属するケーキを返す必要があります。 bakery
とtown
の間の二重下線により、town
のbakery
プロパティにアクセスできます。.order_by("-created_at")
は、作成日順に新しい結果を並べ替えます(_-
_の_"-created_at"
_(マイナス)記号に注意してください。マイナス記号がない場合、それらは順序付けられます。古いものから新しいものへ。[:1]
_は、返されるリストの最初の項目のみを返します(これは、Anytownからのケーキのリストであり、最新のものから順に並べられます)。注:この回答はDjango 1.11向けです。この回答は、Djangoに表示されているクエリ から変更されています] 1.11ドキュメント 。