2010-01-15 88 views
51

我有两个模型AB。所有B对象都有一个A对象的外键。给定一组A对象的,反正是有使用ORM获得包含每个A对象从不同类别获取最新对象的Django查询

这里创建的最新对象的一组B对象是一个简化的例子:

Class Bakery(models.Model): 
    town = models.CharField() 

Class Cake(models.Model): 
    bakery = models.ForeignKey(Bakery) 
    baked_at = models.DateTimeField() 

所以我m查找返回美国Anytown每家面包店烘烤的最新蛋糕的查询。

+6

我很想看到这个:-) – gruszczy 2010-01-15 20:30:42

回答

26

据我所知,有在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_cak‌​e_id', flat=True) 
hottest_cakes = Cake.objects.filter(id__in=hottest_cake_ids) 

BTW学分此去丹尼尔·罗斯曼,谁曾回答我的类似的问题:

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

如果上面的方法太慢,那么我也知道第二种方法 - 您可以编写自定义SQL,只生成相关Bakeries中最热门的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

希望这有助于。

+0

我可能会用你建议的第二组查询。谢谢。 – Zach 2010-01-18 16:08:10

+0

这会更有效率,你使用value_list作为第一个查询: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) – dbn 2014-06-27 22:19:42

+0

此外,如果您碰巧使用PostGreSQL,则有一个解决方案。 – dbn 2015-01-30 12:34:01

4

这应该做的工作:

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

这很好,最后必须学习这些注释。 – gruszczy 2010-01-15 20:37:23

+4

我还没有测试,但看起来它会注释每个面包店最近烤蛋糕的时间。我正在寻找真正的蛋糕对象。我误解了你的答案? – Zach 2010-01-15 21:11:07

+0

@Zach:不,我认为你是对的 – 2010-01-16 07:53:06

15

如果你碰巧使用PostgreSQL,你可以使用Django's interface to DISTINCT ON

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

由于the docs说,你必须order by相同的字段,你distinct on。正如Simon在下面指出的那样,如果你想进行额外的排序,你必须在Python空间中完成。

+0

喜欢这种方式 - 谢谢。刚做了一个关于最终订购的小修改。根据QS的总大小,这可能比接受的答案好或差。在我的情况:更好:) – 2015-01-24 11:48:01

+0

我认为这是一个不必要的代码复杂化,并超出了回答。我将假设人们可以弄清楚如何对结果数据进行排序。 – dbn 2015-01-30 12:29:17

+0

我玩了很多类似的问题,尝试'Max'注释和过滤他们,但他们最终失败,因为在django优化器删除order_by(当使用结果作为筛选子查询或聚合时, .Count之间()')。这个解决方案在获取'recent_cakes.count()'时不会破坏所有的东西,并且在执行'Cake.objects.filter(pk__in = recent_cackes).filter(other_conditions)'时不会抛出错误,但最新的例子返回_random_每个面包店的蛋糕满足other_conditions(不是最热!),因为django从子查询中删除'order_by' :( – 2015-04-01 04:48:15

2

我在与类似的问题斗争,最后得出以下解决方案。它不依赖于order_bydistinct,所以可以根据需要在数据库端进行排序,也可以用作嵌套查询进行过滤。我也相信这个实现是独立于数据库引擎的,因为它基于标准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')) 
4

Django 1.11和感谢开始SubqueryOuterRef,我们可以使用ORM最终建立一个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)) 
0
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",没有减号,他们会用最古老的责令最近。
  • [:1]上年底将在返回列表中只返回第一个项目(这是从北京市东城蛋糕的列表,通过最近的第一排序)

注:这个答案是对的Django 1.11 。查询显示为here in Django 1.11 Docs

相关问题