Django Queryset - 获取每个外键的最新记录

Posted

技术标签:

【中文标题】Django Queryset - 获取每个外键的最新记录【英文标题】:Django Queryset - get the newest record per foreign key 【发布时间】:2020-05-04 15:13:29 【问题描述】:

我看到很多类似的查询,但似乎没有一个适用于我所追求的,

我有一张这样的桌子:

class BGPData(models.Model):
    subnet = models.ForeignKey(Subnet, verbose_name="Subnet", on_delete=models.CASCADE, blank=True, null=True)
    bgp_peer_as = models.CharField(max_length=20, verbose_name='BGP Peer AS', blank=True, null=True)
    bgp_session = models.CharField(max_length=10, verbose_name='BGP Session', blank=True, null=True)
    bgp_routes = models.CharField(max_length=10, verbose_name='BGP Routes Received', blank=True, null=True)
    timestamp = models.DateTimeField(auto_now=True, blank=True, null=True)  

目标: 我只想获取子网上每个外键的最新记录。

目前为止的尝试:-

bgp_data_query = BGPData.objects.filter(subnet__get_bgp=True,subnet__wb_bgp=True) \
                                .annotate(last_updated=Cast(TruncSecond('timestamp', DateTimeField()), CharField()))

d = BGPData.objects.annotate(max_timestamp=Max('timestamp')).filter(timestamp=F('max_timestamp'))

有些东西我还没到这里,但我不确定它是什么......

谢谢

所以如果我的表包含:

subnet_id | routes | timestamp
    1     |   10   | 01-01-20 17:30
    1     |   10   | 01-01-20 17:20
    1     |   10   | 01-01-20 17:10
    2     |   20   | 01-01-20 17:30

我会回来的

subnet_id | routes | timestamp
    1     |   10   | 01-01-20 17:30
    2     |   20   | 01-01-20 17:30

【问题讨论】:

很难理解您要查找的内容。是否要使用 BGPData 模型中的最新时间戳注释的 Subnet 对象? 抱歉,我正在从 bgpdata 表中查找每个 unqiue subnet_id 的最新记录 我添加了示例数据以帮助清理它 【参考方案1】:

一种使用.values() 在使用最大时间戳注释之前按Subnet 分组的方法:

from django.db.models import Max

BGPData.objects.values('subnet').annotate(timestamp=Max('timestamp'))

使用此方法的缺点是结果是一个字典列表,其中仅包含您指定的字段的字段键/值。如果这些字段是您要查找的唯一字段,这不是问题。

如果您需要访问BGPData 模型或相关模型中的更多字段,则可以使用Window function。不幸的是,当我完成此操作时,我意识到 过滤器中不允许使用窗口函数,因此这不能作为解决方案。

from django.db.models import F, Max, Window

data = (
    BGPData.objects
    .annotate(
        max_timestamp=Window(
            expression=Max('timestamp'),
            partition_by=[F('subnet')],
            order_by=F('timestamp').desc(),
        )
    )
    # .filter(timestamp=F('max_timestamp')
)

【讨论】:

好吧,我不确定我现在如何处理实时数据并返回两个结果? 如何获取该查询结果的模型其他字段,例如带有外键和日期的路由?【参考方案2】:

试试这个:

BGPData.objects.filter(timestamp = max(timestamp)).distinct(subnet_id)

【讨论】:

Subnet这个分组怎么样?【参考方案3】:

你在找什么等同于这个用 Django ORM 术语表达的 SQL 查询:

SELECT * FROM bgpdata GROUP BY subnet_id HAVING max(timestamp);

这个查询优雅且易于理解,但看起来在 Django ORM 中没有明显的方法来实现这样的查询。一般来说,您可以编写子查询来检索所有需要的列,如下所示:

from django.db.models import Max

BGPData.objects.filter(
    timestamp__in=BGPData.objects.values('subnet').annotate(
        timestamp__max=Max('timestamp')
    ).values('timestamp__max')
)

【讨论】:

以上是关于Django Queryset - 获取每个外键的最新记录的主要内容,如果未能解决你的问题,请参考以下文章

Django 将带有外键的查询集转换为 JSON

视图中的Django外键 - 获取列表中每个用户显示的第一个图像

具有复杂外键遍历的棘手 Django QuerySet

如何获取Django中子模型中存在外键的父模型的所有对象?

Django讲课笔记10:使用QuerySet删除和查询单表

django queryset过滤外键