簡體   English   中英

從不同類別獲取最新對象的 Django 查詢

[英]Django Query That Get Most Recent Objects From Different Categories

我有兩個模型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()

因此,我正在尋找一個查詢,該查詢返回在美國 Anytown 的每個面包店中烘焙的最新蛋糕。

從開始Django 1.11和感謝子查詢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))

據我所知,在 Django ORM 中沒有一步到位的方法。

但是您可以將其拆分為兩個查詢:

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 時間戳一起進行,您可以簡化和消除上述代碼的歧義(如果兩個蛋糕同時到達,您可以同時獲得它們):

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)

順便說一句,這要歸功於 Daniel Roseman,他曾經回答過我的類似問題:

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

如果上述方法太慢,那么我也知道第二種方法 - 您可以編寫僅生成相關 Bakeries 中最熱門的 Cakes 的自定義 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

希望這會有所幫助。

如果您碰巧使用 PostGreSQL,則可以使用Django 的 DISTINCT ON 接口

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

正如文檔所說,您必須order bydistinct on的相同字段進行order by 正如西蒙在下面指出的那樣,如果您想進行額外的排序,則必須在 Python 空間中進行。

這應該可以完成這項工作:

from django.db.models import Max
Bakery.objects.annotate(Max('cake__baked_at'))

我正在與類似的問題作斗爭,最后得出以下解決方案。 它不依賴於order_bydistinct因此可以在 db 端根據需要進行排序,也可以用作嵌套查詢進行過濾。 我也相信這個實現是獨立於數據庫引擎的,因為它基於標准的 sql HAVING子句。 唯一的缺點是它會在每個面包店返回多個最熱的蛋糕,如果它們是在同一時間在那個面包店烘焙的。

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”的蛋糕,假設國家不是字符串的一部分。 bakerytown之間的雙下划線允許我們訪問bakerytown屬性。
  • .order_by("-created_at")將按創建日期對結果進行排序,最近的在前(注意- (減號)在"-created_at"的符號。如果沒有減號,它們"-created_at"最舊到最多排序最近。
  • [:1]最后將只返回返回列表中的第一個項目(這將是來自 Anytown 的蛋糕列表,按最近的排序)。

注意:此答案適用於 Django 1.11。 這個答案修改自Django 1.11 Docs 中顯示的查詢。

上面的@Tomasz Zieliński 解決方案確實解決了您的問題,但沒有解決我的問題,因為我仍然需要過濾蛋糕。 所以這是我的解決方案

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]
)

使用這種方法,您還可以實現其他功能,例如蛋糕的過濾器、排序、分頁

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM