20

我在 Django 中有以下模型模式(使用 Postgres)。

class A(Models.model):
    related = models.ManyToManyField("self", null=True)

给定一个 A 的 QuerySet,我想尽快返回一个字典,将 QuerySet 中的每个实例映射到其实例的sA列表。idrelated

我当然可以遍历每个 A 并查询相关字段,但是有更优化的方法吗?

4

2 回答 2

19

根据你有三个实例。您可以使用该values_list方法仅检索结果,并从该结果中仅获取其related实例的 ID。我使用该pk字段作为我的过滤器,因为我不知道你的方案,但你可以使用任何东西,只是必须是QuerySet.

>>> result = A.objects.filter(pk=1)
>>> result.values('related__id')
[{'id': 2}, {'id': 3}]
>>> result.values_list('related__id')
[(2,), (3,)]
>>> result.values_list('related__id', flat=True)
[2, 3]
于 2013-06-30T09:52:30.047 回答
1

You can get pretty close like this:

qs = A.objects.prefetch_related(Prefetch(
                      'related', 
                      queryset=A.objects.only('pk'), 
                      to_attr='related_insts')).in_bulk(my_list_of_pks)

This will give a mapping from pks of the current object to the instance itself, so you can iterate through as follows:

for pk, inst in qs.iteritems():
  related_ids = (related.pk for related in inst.related_insts)

Or given an instance, you can do a fast lookup like so:

related_ids = (related.pk for related in qs[instance.pk]).

This method maps the instance ids to the related ids (indirectly) since you specifically requested a dictionary. If you aren't doing lookups, you may want the following instead:

qs = A.objects.prefetch_related(Prefetch(
        'related', 
        queryset=A.objects.only('pk'), 
        to_attr='related_insts')).filter(pk__in=my_list_of_pks)
for inst in qs:
  related_ids = (related.pk for related in inst.related_insts)

You may take note of the use of only to only pull the pks from the db. There is an open ticket to allow the use of values and (I presume) values_list in Prefetch queries. This would allow you to do the following.

qs = A.objects.prefetch_related(Prefetch(
        'related', 
        queryset=A.objects.values_list('pk', flat=True), 
        to_attr='related_ids')).filter(pk__in=my_list_of_pks)
for inst in qs:
  related_ids = inst.related_ids

You could of course optimize further, for example by using qs.only('related_insts') on the primary queryset, but make sure you aren't doing anything with these instances-- they're essentially just expensive containers to hold your related_ids.

I believe this is the best that's available for now (without custom queries). To get to exactly what you want, two things are needed:

  1. The feature above is implemented
  2. values_list is made to work with Prefetch to_attr like it does for annotations.

With these two things in place (and continuing the above example) you could do the following to get exactly what you requested:

d = qs.values_list('related_ids', flat=True).in_bulk()
for pk, related_pks in d.items():
    print 'Containing Objects %s' % pk
    print 'Related objects %s' % related_pks
# And lookups
print 'Object %d has related objects %s' % (20, d[20])

I've left off some details explaining things, but it should be pretty clear from the documentation. If you need any clarification, don't hesitate!

于 2017-08-08T17:48:59.457 回答