81

私は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()

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

4

7 に答える 7

39

SubqueryOuterRefから始めてDjango 1.11、おかげで、最終的にを使用してクエリを作成できます。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))
于 2017-05-11T22:15:21.213 に答える
36

私の知る限り、Django ORMでこれを行うワンステップの方法はありませんが、2つのクエリに分割できます。

from django.db.models import Max

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つのケーキが同時に到着した場合は、両方を取得できます)。

from django.db.models import Max

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)

ところで、これに対するクレジットは、かつて私の同様の質問に答えたダニエル・ローズマンに行きます:

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

お役に立てれば。

于 2010-01-16T08:12:43.717 に答える
23

PostGreSQLを使用している場合は、Djangoのインターフェースを使用してDISTINCTONを実行できます。

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

ドキュメントにあるように、あなたはあなたorder byと同じフィールドでなければなりませんdistinct on。Simonが以下で指摘しているように、追加の並べ替えを行う場合は、Python空間で行う必要があります。

于 2013-11-21T18:35:42.143 に答える
5

これでうまくいくはずです:

from django.db.models import Max
Bakery.objects.annotate(Max('cake__baked_at'))
于 2010-01-15T20:34:44.510 に答える
4

私は同様の問題と戦っていましたが、最終的に次の解決策に到達しました。order_by依存しないためdistinct、db側で必要に応じて並べ替えることができ、フィルタリング用のネストされたクエリとしても使用できます。また、この実装は標準のsqlHAVING句に基づいているため、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'))
于 2015-04-01T12:07:08.333 に答える
0
Cake.objects.filter(bakery__town="Anytown").order_by("-created_at")[:1]

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

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

注:この回答はDjango1.11用です。 この回答は、Django1.11Docs示されているクエリから変更されています。

于 2017-05-25T09:12:48.787 に答える
0

上記の@TomaszZielińskiソリューションは問題を解決しましたが、Cakeをフィルタリングする必要があるため、私の問題は解決しませんでした。これが私の解決策です

from django.db.models import Q, Max

hottest_yellow_round_cake = Max('cake__baked_at', filter=Q(cake__color='yellow', cake__shape='round'))

bakeries = Bakery.objects.filter(town='Chicago').annotate(
    hottest_cake_baked_at=hottest_yellow_round_cake
)

hottest_cakes = Cake.objects.filter(
    baked_at__in=[b.hottest_cake_baked_at for b in bakeries]
)

このアプローチでは、ケーキのフィルター、順序付け、ページ付けなどの他のものを実装することもできます

于 2020-11-19T08:07:25.670 に答える