获取不同类别中最新对象的Django查询
我有两个模型,分别叫做 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()
所以我想要一个查询,返回在美国任何城镇的每个面包店最近烤出的蛋糕。
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_cake_id', flat=True)
hottest_cakes = Cake.objects.filter(id__in=hottest_cake_ids)
顺便提一下,这个方法要感谢Daniel Roseman,他曾经回答过我类似的问题:
如果上面的方法太慢了,我还知道第二种方法——你可以写自定义的SQL,只获取那些在相关烘焙店中最热门的蛋糕,把它定义为数据库视图,然后为它写一个不受管理的Django模型。这在上面的django-users讨论中也提到过。原始概念的直接链接在这里:
希望这对你有帮助。