Django:获取对象列表的相关记录列表

Sil*_*ght 5 django foreign-keys

我有两个与一对多相关的模型:

class Person(models.Model):
    name      = models.CharField(max_length=255);
    surname   = models.CharField(max_length=255);     
    age       = models.IntegerField(); 


class Dog(models.Model):
    name      = models.CharField(max_length=255);
    owner     = models.ForeignKey('Person');
Run Code Online (Sandbox Code Playgroud)

我想输出一份人员名单,并在每个人的下方列出他所拥有的狗.这是我如何做到的:

在视图中:

persons = Person.objects.all()[0:100];
Run Code Online (Sandbox Code Playgroud)

在模板中:

{% for p in persons %}
    {{ p.name }} has dogs:<br />
    {% for d in persons.dog_set.all %}
        - {{ d.name }}<br />
    {% endfor %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做,Django将执行101个SQL查询,效率非常低.

我试图创建一个自定义管理器,它将获取所有人,然后所有的狗并在python中链接它们,但后来我不能使用paginator(我的另一个问题:Django:Paginator +原始SQL查询)它看起来很丑陋.

这样做有更优雅的方式吗?

UPDATE

解决方案基于@Daniel Roseman的博客条目

all_objects = Person.objects.select_related().all();

paginator = Paginator(all_objects, 100);

try:
    page = int(request.GET.get('page', '1'))
except ValueError:
    page = 1

try:
    current_page = paginator.page(page)
except (EmptyPage, InvalidPage):
    current_page = paginator.page(paginator.num_pages)

person_list = dict([(obj.id, obj) for obj in current_page.object_list])
id_list = [obj.id for obj in current_page.object_list]

objects = Dog.objects.filter(owner__id__in=id_list)

relation_dict = {}
for obj in objects:
    relation_dict.setdefault(obj.owner_id, []).append(obj)

for id, related_items in relation_dict.items():
    person_list[id].dogs = related_items
Run Code Online (Sandbox Code Playgroud)

Bob*_*Bob 3

使用较新版本的 Django(>=1.7 甚至可能是一些较旧的版本),您只需替换您的

persons = Person.objects.all()[0:100]
Run Code Online (Sandbox Code Playgroud)

persons = Person.objects.prefetch_related('dog_set').all()[0:100]
Run Code Online (Sandbox Code Playgroud)

就是这样,如果您使用的是 Django>=1.7,则无需继续使用旧的解决方法

官方文档prefetch_related在这里: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#prefetch-lated