Django Queryset - 获取每个外键的最新记录
Django Queryset - get the newest record per foreign key
我看到很多类似的查询,但 none 似乎正在为我所追求的工作,
我有一个 table 这样的:
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)
Objective:
我只想获取子网上每个外键的最新记录。
到目前为止的尝试次数:-
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'))
这里有些东西我不太明白,但我不确定它是什么...
谢谢
所以如果我的 table 包含:
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
我会return
subnet_id | routes | timestamp
1 | 10 | 01-01-20 17:30
2 | 20 | 01-01-20 17:30
试试这个:
BGPData.objects.filter(timestamp = max(timestamp)).distinct(subnet_id)
在使用最大时间戳注释之前使用 .values()
按 Subnet
分组的一种方法:
from django.db.models import Max
BGPData.objects.values('subnet').annotate(timestamp=Max('timestamp'))
使用此方法的缺点是结果是一个字典列表,其中仅包含您指定字段的字段 keys/values。如果这些字段是您要查找的唯一字段,这不是问题。
如果您需要访问 BGPData
模型或相关模型中的更多字段,则可以使用 Window
function。不幸的是,当我完成这个时,我意识到 filter 中不允许 window 函数,所以这不能作为解决方案。
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')
)
您要查找的内容等同于此 SQL 以 Django ORM 术语表达的查询:
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')
)
我看到很多类似的查询,但 none 似乎正在为我所追求的工作,
我有一个 table 这样的:
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)
Objective: 我只想获取子网上每个外键的最新记录。
到目前为止的尝试次数:-
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'))
这里有些东西我不太明白,但我不确定它是什么...
谢谢
所以如果我的 table 包含:
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
我会return
subnet_id | routes | timestamp
1 | 10 | 01-01-20 17:30
2 | 20 | 01-01-20 17:30
试试这个:
BGPData.objects.filter(timestamp = max(timestamp)).distinct(subnet_id)
在使用最大时间戳注释之前使用 .values()
按 Subnet
分组的一种方法:
from django.db.models import Max
BGPData.objects.values('subnet').annotate(timestamp=Max('timestamp'))
使用此方法的缺点是结果是一个字典列表,其中仅包含您指定字段的字段 keys/values。如果这些字段是您要查找的唯一字段,这不是问题。
如果您需要访问 BGPData
模型或相关模型中的更多字段,则可以使用 Window
function。不幸的是,当我完成这个时,我意识到 filter 中不允许 window 函数,所以这不能作为解决方案。
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')
)
您要查找的内容等同于此 SQL 以 Django ORM 术语表达的查询:
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')
)