如何减少django模型中的查询has_relation方法?

时间:2011-06-01 20:54:28

标签: python django django-models django-queryset

以下是两个Django模型示例。要特别注意has_pet方法。

class Person(models.Model):
    name = models.CharField(max_length=255)

    def has_pet(self):
        return bool(self.pets.all().only('id'))

class Pet(models.Model):
    name = models.CharField(max_length=255)
    owner = models.ForeignKey(Person, blank=True, null=True, related_name="pets")

这里的问题是has_pet方法总是生成一个查询。如果你做这样的事情。

p = Person.objects.get(id=1)
if p.has_pet():
    ...

然后你实际上会做一个额外的查询,只是为了检查一个人是否有宠物。如果你需要检查多个人,这是一个大问题。如果在这样的模板中使用它,它也会生成查询。

{% for person in persons %}
    {% if person.has_pet %}
        {{ person.name }} owns a pet
    {% else %}
        {{ person.name }} is petless
    {% endif %}
{% endfor %}

此示例实际上会在呈现模板时为人员查询集中的每个人执行额外查询。

有没有办法只使用一个查询来执行此操作,或者每个人至少执行少于一个额外查询?也许还有另一种设计方法可以完全避免这个问题。

我想到为Person添加一个BooleanField,并且每当保存或删除宠物时都会更新该字段。这真的是正确的方法吗?

此外,我已经正确地进行了memcached设置,因此只有在结果尚未缓存的情况下才会发生这些查询。我想首先删除查询以进行更优化。

1 个答案:

答案 0 :(得分:4)

如果您想要所有有宠物的人的列表,您可以在一个查询中执行此操作:

Person.objects.exclude(pets=None)

听起来你想迭代一个人列表,使用注释可能有意义:

for person in Person.objects.annotate(has_pet=Count('pets')):
     if person.has_pet: # if has_pet is > 0 this is True, no extra query

如果Django有一个Exists聚合但是它没有,那就太好了,我不知道添加一个是多么困难。你当然应该剖析,并弄清楚这是否适合你。

就个人而言,我可能只是将has_pets作为布尔值存储在模型上,它可能是最有效的方法。