首页 > 解决方案 > django - 只预取最新的记录?

问题描述

我正在尝试仅针对父记录预取最新记录。

我的模型就是这样

class LinkTargets(models.Model):
    device_circuit_subnet = models.ForeignKey(DeviceCircuitSubnets, verbose_name="Device", on_delete=models.PROTECT)
    interface_index = models.CharField(max_length=100, verbose_name='Interface index (SNMP)', blank=True, null=True)
    get_bgp = models.BooleanField(default=False, verbose_name="get BGP Data?")
    dashboard = models.BooleanField(default=False, verbose_name="Display on monitoring dashboard?")


class LinkData(models.Model):
    link_target = models.ForeignKey(LinkTargets, verbose_name="Link Target", on_delete=models.PROTECT)
    interface_description = models.CharField(max_length=200, verbose_name='Interface Description', blank=True, null=True)
...

以下查询失败并出现错误

AttributeError: 'LinkData' object has no attribute '_iterable_class'

询问:

link_data = LinkTargets.objects.filter(dashboard=True) \
                            .prefetch_related(
                                Prefetch(
                                    'linkdata_set',
                                    queryset=LinkData.objects.all().order_by('-id')[0]
                                    )
                                )

我考虑过获取 LinkData 并进行相关选择,但我不知道如何为每个 link_target_id 仅获取 1 条记录

link_data = LinkData.objects.filter(link_target__dashboard=True) \
                            .select_related('link_target')..?   

编辑:

使用 rtindru 的解决方案,预取的似乎是空的。目前有 6 条记录,为 3 个 LinkTargets 中的每一个测试 1 条记录

>>> link_data[0]
<LinkTargets: LinkTargets object>
>>> link_data[0].linkdata_set.all()
<QuerySet []>
>>>

标签: pythondjangodjango-queryset

解决方案


原因是Prefetch需要一个 DjangoQueryset作为queryset参数,而您正在提供一个对象的实例。

更改您的查询如下:

link_data = LinkTargets.objects.filter(dashboard=True) \
                            .prefetch_related(
                                Prefetch(
                                    'linkdata_set',
                                    queryset=LinkData.objects.filter(pk=LinkData.objects.latest('id').pk)
                                    )
                                )

Prefetch这确实具有在很大程度上破坏目的的不幸效果。

更新 这会在全局范围内预取一条记录;不是最新的LinkData记录LinkTarget

要为每个 LinkTarget 预取最大 LinkData,您应该从 LinkData 开始:您可以按如下方式实现:

LinkData.objects.filter(link_target__dashboard=True).values('link_target').annotate(max_id=Max('id'))

这将返回 { link_target: 12, max_id: 3223}的字典

然后,您可以使用它返回正确的对象集;也许过滤 LinkData 基于max_id.

这看起来像这样:

latest_link_data_pks = LinkData.objects.filter(link_target__dashboard=True).values('link_target').annotate(max_id=Max('id')).values_list('max_id', flat=True)
link_data = LinkTargets.objects.filter(dashboard=True) \
                            .prefetch_related(
                                Prefetch(
                                    'linkdata_set',
                                    queryset=LinkData.objects.filter(pk__in=latest_link_data_pks)
                                    )
                                )   

推荐阅读