Django只预取最新的记录?

2024-09-29 17:15:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图根据父记录只预取最新的记录。在

我的模特就是这样

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)
...

以下查询因错误而失败

^{pr2}$

查询:

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

我想改为获取LinkData并做一个select相关的操作,但是我不知道如何为每个link_target_id获取1条记录

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

编辑:

使用rtindru的解决方案,pre-fetch似乎是空的。其中目前有6条记录,3条链接目标的每条记录各1条

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

Tags: nametruetargetverbosedatamodelobjectson
2条回答

LinkData.objects.all().order_by('-id')[0]不是查询集,它是模型对象,因此出现错误。在

您可以尝试LinkData.objects.all().order_by('-id')[0:1],这确实是一个查询集,但它行不通。给定prefetch_related的工作方式,queryset参数必须返回一个包含所需的所有LinkData记录的queryset(然后进一步过滤,其中的项与LinkTarget对象连接起来)。这个查询集只包含一个项,所以这是不好的。(Django会抱怨“一旦获取了一个片段就无法过滤查询”,并引发了一个异常,因为它应该这样做)。在

我们退后。实际上,你在问一个聚合/注释问题——对于每个LinkTarget,你想知道最新的LinkData对象,或者“id”列的“max”。最简单的方法是只使用id进行注释,然后进行单独的查询以获取所有对象。在

因此,它应该是这样的(我在我的项目中使用了一个类似的模型,所以它应该可以工作,但是下面的代码可能有一些错误):

linktargets = (LinkTargets.objects
               .filter(dashboard=True)
               .annotate(most_recent_linkdata_id=Max('linkdata_set__id'))

# Now, if we need them, lets collect and get the actual objects
linkdata_ids = [t.most_recent_linkdata_id for t in linktargets]
linkdata_objects = LinkData.objects.filter(id__in=linkdata_ids)

# And we can decorate the LinkTarget objects as well if we want:

linkdata_d = {l.id: l for l in linkdata_objects}
for t in linktargets:
    if t.most_recent_linkdata_id is not None:
        t.most_recent_linkdata = linkdata_d[t.most_recent_linkdata_id]

我故意将其放入一个预取中,屏蔽linkdata_set,因为结果是你有欺骗你的对象,linkdata_set属性现在缺少结果。你真的想被它咬下去吗?最好创建一个新的属性,它正好拥有你想要的东西。在

原因是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)
                                    )
                                )

这确实有一个不幸的结果,即在很大程度上取消了{}的目的。在

更新 此操作只全局预取一条记录;而不是每个LinkTarget最新的LinkData记录。在

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

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

这将返回一个{{}:12、max_id:3223}的字典

然后可以使用它返回正确的对象集;也许可以根据max_id的值过滤LinkData。在

看起来像这样:

^{pr2}$

相关问题 更多 >

    热门问题