我正在尝试仅针对父记录预取最新记录。
我的模特就是这样
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 []>
>>>
答案 0 :(得分:4)
原因是Queryset
期望Django 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
的最新LinkData.objects.filter(link_target__dashboard=True).values('link_target').annotate(max_id=Max('id'))
条记录。
要为每个LinkTarget预取最大LinkData,您应该从LinkData开始:您可以按如下方式实现:
link_target
这将返回{max_id
:12,max_id
的字典:3223}
然后您可以使用它来返回正确的对象集;也许根据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)
)
)
。
看起来像这样:
{{1}}
答案 1 :(得分:1)
以下内容适用于PostgreSQL。我了解它不会帮助OP,但是对其他人可能有用。
from django.db.models import Count, Prefetch
from .models import LinkTargets, LinkData
link_data_qs = LinkData.objects.order_by(
'link_target__id',
'-id',
).distinct(
'link_target__id',
)
qs = LinkTargets.objects.prefetch_related(
Prefetch(
'linkdata_set',
queryset=link_data_qs,
)
).all()
答案 2 :(得分:0)
LinkData.objects.all().order_by('-id')[0]
不是查询集,它是模型对象,因此是您的错误。
您可以尝试LinkData.objects.all().order_by('-id')[0:1]
这确实是一个QuerySet,但它无法正常工作。鉴于prefetch_related
如何工作,queryset参数必须返回一个包含所需的所有LinkData
记录的查询集(然后进一步过滤,并使用LinkTarget对象将其中的项连接起来)。此查询集仅包含一个项目,因此不合适。 (并且Django会抱怨&#34;一旦切片被采取后就无法过滤查询&#34;并且应该提出异常)。
让我们回来吧。基本上你要问一个聚合/注释问题 - 对于每个LinkTarget,你想要知道最新的LinkData对象,或者&#39; max&#39;一个&#39; id&#39;柱。最简单的方法是使用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
属性现在丢失了结果。你真的想被那个地方的某个地方所困扰吗?最好制作一个只有您想要的新属性。
答案 3 :(得分:0)
棘手,但似乎有效:
class ForeignKeyAsOneToOneField(models.OneToOneField):
def __init__(self, to, on_delete, to_field=None, **kwargs):
super().__init__(to, on_delete, to_field=to_field, **kwargs)
self._unique = False
class LinkData(models.Model):
# link_target = models.ForeignKey(LinkTargets, verbose_name="Link Target", on_delete=models.PROTECT)
link_target = ForeignKeyAsOneToOneField(LinkTargets, verbose_name="Link Target", on_delete=models.PROTECT, related_name='linkdata_helper')
interface_description = models.CharField(max_length=200, verbose_name='Interface Description', blank=True, null=True)
link_data = LinkTargets.objects.filter(dashboard=True) \
.prefetch_related(
Prefetch(
'linkdata_helper',
queryset=LinkData.objects.all().order_by('-id'),
'linkdata'
)
)
# Now you can access linkdata:
link_data[0].linkdata
当然,使用这种方法您不能使用 linkdata_helper 来获取相关对象。