【问题标题】:How to reduce queries in django model has_relation method?如何减少 django 模型 has_relation 方法中的查询?
【发布时间】:2011-06-01 20:54:28
【问题描述】:

这里有两个示例 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 %}

这个例子实际上会在渲染模板时对persons查询集中的每个人执行一个额外的查询。

有没有办法只用一个查询,或者至少每人只做不到一个额外的查询?也许还有另一种设计方法可以完全避免这个问题。

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

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

【问题讨论】:

  • 我认为您的建议是最好的方法:一个跟踪用户是否有宠物的布尔字段。 Pet 是数据库中的一个单独的表,因此您总是需要对其执行某种查询来查看 Person 是否有 Pet
  • 虽然,现在我想起来了,如果你查询 Pet 表中的所有条目,你不应该对每个人都执行后续查询。即如果 Pet.objects.all(owner.id=id).exists() 不应该导致数据库上的另一个查询

标签: python django django-models django-queryset


【解决方案1】:

如果您想要一份所有养宠物的人的列表,您可以在一个查询中完成:

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 存储为模型上的布尔值,这可能是最有效的方法。

【讨论】:

  • 好的,根据我的示例,我可以看到您可能会如何认为这是我所要求的,但我已经知道了。我将更新我的示例,以免给人这种错误的印象。
  • 注释绝对可以将其减少为一个查询。可悲的是,当我有四五个这样的 has_ 检查时,一个查询非常慢。它是布尔字段!
猜你喜欢
  • 2010-12-14
  • 2014-02-07
  • 2023-02-17
  • 1970-01-01
  • 2020-06-02
  • 2020-03-25
  • 2011-09-24
  • 2018-01-08
  • 1970-01-01
相关资源
最近更新 更多