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

85 投票
7 回答
25471 浏览
提问于 2025-04-15 18:04

我有两个模型,分别叫做 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()

所以我想要一个查询,返回在美国任何城镇的每个面包店最近烤出的蛋糕。

7 个回答

25

如果你正在使用PostGreSQL数据库,可以利用Django提供的DISTINCT ON功能

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

正如文档中所说,你需要对和你使用distinct on的字段进行order by排序。正如Simon在下面提到的,如果你想要进行额外的排序,就需要在Python中处理。

41

Django 1.11 开始,得益于 子查询外部引用,我们终于可以使用 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))
39

据我所知,在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和烘焙时间是按顺序排列的,你可以简化并澄清上面的代码(如果两个蛋糕同时到达,你可以获取到这两个蛋糕):

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=

如果上面的方法太慢了,我还知道第二种方法——你可以写自定义的SQL,只获取那些在相关烘焙店中最热门的蛋糕,把它定义为数据库视图,然后为它写一个不受管理的Django模型。这在上面的django-users讨论中也提到过。原始概念的直接链接在这里:

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

希望这对你有帮助。

撰写回答