查询优化:howto

时间:2016-01-18 16:46:09

标签: django django-models

我的模特是这样的:

class Personne(BaseModel):
    # [skip] many fields then:
    photos = models.ManyToManyField(Photo, blank=True,
                                    through='PersonnePhoto',
                                    symmetrical=False,
                                    related_name='parent')
    def photo_profil(self):
        a = PersonnePhoto.objects.filter(
            personne=self, photo_type=PersonnePhoto.PHOTO_PROFIL)
        return a[0] if len(a) else None

    # [skip] many fields then:
    travels = models.ManyToManyField(
            TagWithValue, blank=True,
            through='PersonneTravel',
            default=None, symmetrical=False,
            related_name='personne_travel')

class PersonneRelation(BaseModel):
    src = models.ForeignKey('Personne', related_name='src')
    dst = models.ForeignKey('Personne', related_name='dst')
    opposite = models.ForeignKey('PersonneRelation',
                                 null=True, blank=True, default=None)
    is_reverse = models.BooleanField(default=False)

我需要显示一个人的所有联系人,并为每个联系人显示他/她的所有旅行和他/她的照片。

以下是我的观点中的代码:

class IndexView(LoginRequiredMixin, generic.TemplateView):
    template_name = 'my_home/contacts/index.html'

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        context['contacts'] = Personne.objects.get(
                user=self.request.user).relations.all()
        return context

非常简单。

问题出在我的模板中。 my_home/contacts/index.html为每位联系人提供contact_detail.html

    {% for c in contacts %}
        {% with c as contact %}
            {% include 'includes/contact_detail.html' %}
        {% endwith %}
    {% endfor %}

contact_detail.html中,我调用photo_profil,它会进行查询以获取联系人图片的值。

{{ contact.photo_profil }}

这意味着,如果用户有100个联系人,我会为所有联系人执行 一个查询,然后为每个联系人执行100个查询。如何优化这个?我对旅行有同样的问题,实际上,联系人的每个ManyToMany字段也存在同样的问题。

1 个答案:

答案 0 :(得分:1)

看起来你需要一些prefetch_related善良:

context['contacts'] = (Personne.objects
                       .get(user=self.request.user)
                       .relations
                       .all()
                       .prefetch_related('travels', 'photos'))

但请注意,它会获取所有联系人的照片,这是您不希望的。基本上你有两个选择。将一些毛茸茸的原始SQL添加到Prefetch对象的queryset参数中。或者(正如我在其中一个项目中所做的那样)指定一个单独的main_photo字段用于存储用户的头像(单独的ForeignKeyPhoto,我的意思是,不是完全独立的一个) 。是的,它显然是非规范化,但查询变得很多更简单。毕竟,您的用户可以设置主照片。