代码之家  ›  专栏  ›  技术社区  ›  Zach

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

  •  61
  • Zach  · 技术社区  · 15 年前

    我有两种型号 A B . 全部的 B 对象的外键是 A. A. 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()
    

    因此,我正在寻找一个查询,返回美国Anytown每个面包店最近烘焙的蛋糕。

    6 回复  |  直到 5 年前
        1
  •  36
  •   dKen baraa yusri    3 年前

    据我所知,在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]
    )
    

    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)
    

    顺便说一句,这要归功于丹尼尔·罗斯曼,他曾经回答过我类似的问题:

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

    如果上面的方法太慢,那么我也知道第二种方法——您可以编写定制SQL,只生成那些在相关面包店最热的蛋糕,将其定义为数据库视图,然后为其编写非托管Django模型。上面的django用户线程中也提到了它。与原始概念的直接链接如下:

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

    希望这有帮助。

        2
  •  38
  •   Todor    7 年前

    Django 1.11 多亏了 Subquery 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))
    
        3
  •  22
  •   dbn    10 年前

    如果您碰巧正在使用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空间中进行。

        4
  •  5
  •   Daniel Roseman    15 年前

    from django.db.models import Max
    Bakery.objects.annotate(Max('cake__baked_at'))
    
        5
  •  4
  •   Ivan Klass    9 年前

    我在与类似的问题作斗争,最终得出以下解决方案。它不依赖于 order_by distinct 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'))
    
        6
  •  0
  •   twk    7 年前
    Cake.objects.filter(bakery__town="Anytown").order_by("-created_at")[:1]
    

    我还没有建立起我这边的模型,但从理论上讲,这应该是可行的。细分:

    • Cake.objects.filter(bakery__town="Anytown") 如果国家不在其中,则应退还属于“任何城镇”的任何蛋糕。双下划线在 bakery town 性质 面包店 .
    • .order_by("-created_at") 将按结果的创建日期排序,最新日期排在第一位(注意 - (减)登录 "-created_at" . 如果没有减号,它们将按从最老到最近的顺序排列。
    • [:1] 最后将只返回列表中返回的第一项(这将是来自任何城镇的蛋糕列表,按最近的第一项排序)。

    注:此答案适用于Django 1.11。 here in Django 1.11 Docs

        7
  •  0
  •   Nguyen Anh Vu    4 年前

    @上面的Tomasz Zieliski解决方案确实解决了您的问题,但并没有解决我的问题,因为我仍然需要过滤蛋糕。这就是我的解决方案

    from django.db.models import Q, Max
    
    hottest_yellow_round_cake = Max('cake__baked_at', filter=Q(cake__color='yellow', cake__shape='round'))
    
    bakeries = Bakery.objects.filter(town='Chicago').annotate(
        hottest_cake_baked_at=hottest_yellow_round_cake
    )
    
    hottest_cakes = Cake.objects.filter(
        baked_at__in=[b.hottest_cake_baked_at for b in bakeries]
    )
    

    通过这种方法,您还可以实现其他功能,如过滤、排序和蛋糕分页