从不同类别获取最新对象的Django查询
我有两个模型A
和B
所有的B
对象都有一个到A
对象的外键。 给定一组A
对象,是否有使用ORM来获取包含为每个A
对象创build的最近对象的一组B
对象
这是一个简单的例子:
Class Bakery(models.Model): town = models.CharField() Class Cake(models.Model): bakery = models.ForeignKey(Bakery) baked_at = models.DateTimeField()
所以我正在寻找一个查询,返回美国Anytown每家面包店出炉的最新蛋糕。
据我所知,在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])
如果蛋糕的id和bake_at时间戳一起进行,可以简化和消除上面的代码(如果两个蛋糕同时到达,您可以同时获得):
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)
顺便说一下,丹尼尔·罗斯曼曾经回答过类似的问题:
如果上面的方法太慢,那么我也知道第二种方法 – 你可以编写自定义的SQL,只生成相关面包房中最热门的Cakes,将其定义为数据库VIEW,然后为其编写非托pipeDjango模型。 在上面的django-users线程中也提到了这一点。 直接链接到原来的概念是这里:
希望这可以帮助。
如果您碰巧使用PostGreSQL,则可以使用Django的界面DISTINCT ON :
recent_cakes = Cake.objects.order_by('bakery__id', '-baked_at').distinct('bakery__id')
正如文件所说,你必须order by
与你distinct on
领域order by
。 正如Simon在下面指出的那样,如果您想进行额外的sorting,则必须在Python空间中执行此操作。
这应该做的工作:
from django.db.models import Max Bakery.objects.annotate(Max('cake__baked_at'))
从Django 1.11
开始,感谢Subquery和OuterRef ,最后我们可以使用ORM
构build一个latest-per-group
查询。
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))
我正在与类似的问题斗争,最后得出以下解决scheme。 它不依赖于order_by
和distinct
所以可以根据需要在db端进行sorting,也可以用作嵌套查询进行过滤。 我也相信这个实现是独立于数据库引擎的,因为它基于标准的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]
我没有build立我的模型,但理论上这应该工作。 分解:
-
Cake.objects.filter(bakery__town="Anytown")
假定该国不属于任何string,应归还属于“Anytown”的所有蛋糕。bakery
和town
之间的双重下划线允许我们访问bakery
的town
财产。 -
.order_by("-created_at")
会根据结果的创builddate,最近的第一个(注意"-created_at"
中的-
(减号)符号),如果没有减号,最近。 -
[:1]
在结束时将只返回列表中返回的第一个项目(这将是来自Anytown的蛋糕列表,按照最近的sorting)。
注意:这个答案是为Django 1.11。 这个答案从Django 1.11文档中的查询修改。