Django查询从不同类别获取最新对象

Zac*_*ach 72 python django django-queryset greatest-n-per-group

我有两个型号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()
Run Code Online (Sandbox Code Playgroud)

所以我正在寻找一个可以返回美国Anytown每家面包店最新蛋糕的查询.

Tom*_*ski 31

据我所知,在Django ORM中没有一步到位的方法.

但是您可以在两个查询中拆分它:

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]
)
Run Code Online (Sandbox Code Playgroud)

如果蛋糕的id正在与bake_at时间戳一起进行,您可以简化和消除上述代码的歧义(如果两个蛋糕同时到达,您可以同时获得它们):

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)
Run Code Online (Sandbox Code Playgroud)

BTW为此归功于丹尼尔罗斯曼,曾经回答了我的类似问题:

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

如果上面的方法太慢,那么我也知道第二种方法 - 你可以编写自定义SQL,只生成那些在相关面包店中最热门的Cakes,将其定义为数据库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

希望这可以帮助.

  • 第一个解决方案是否会产生一个问题,即一个的最新日期在另一个的最新日期之前,但在另一个日期之前存在?A = [1,2,3],B = [1,2].最新的= 3,B最新= 2.第一个查询似乎得到A的2和3,以及B的2. (2认同)

Tod*_*dor 23

从开始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))
Run Code Online (Sandbox Code Playgroud)


dbn*_*dbn 18

如果你碰巧使用的是PostGreSQL,你可以使用Django的DISTINCT ON接口:

recent_cakes = Cake.objects.order_by('bakery__id', '-baked_at').distinct('bakery__id')
Run Code Online (Sandbox Code Playgroud)

正如文档所说,你必须order by和你一样distinct on.正如Simon在下面指出的那样,如果你想进行额外的排序,你必须在Python空间中进行.


Dan*_*man 5

这应该做的工作:

from django.db.models import Max
Bakery.objects.annotate(Max('cake__baked_at'))
Run Code Online (Sandbox Code Playgroud)

  • 我还没有测试过,但看起来它会注释每个面包店最近烘焙蛋糕的时间.我正在寻找实际的蛋糕对象.我误解了你的答案吗? (4认同)