如何减少Django模型has_relation方法中的查询?
这里有两个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,所以那些查询只有在结果没有被缓存时才会发生。我希望能从根本上减少查询,以实现更好的优化。
1 个回答
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
的布尔值,这可能是最有效的做法。