django中的Prefetch_related和select_related函数
在Django中,select_related和prefetch_related被设计用来阻止因访问相关对象而导致的数据库查询的泛滥。在这篇文章中,我们将看到它是如何减少查询次数并使程序更快。
- select_related() “遵循 “外键关系,当它执行查询时选择额外的相关对象数据。
- prefetch_related()为每个关系做单独的查找,并在Python中做 “连接”。
当你要选择的对象是一个单一的对象时,就使用select_related,所以是OneToOneField或ForeignKey。当你要得到一个 “集合 “的东西时,你使用prefetch_related,所以像你说的ManyToManyFields或反向外键。澄清一下我说的 “反向外键 “是什么意思。
举例说明Prefetch_related和select_related的概念 –
上面的分类可能不是很清楚,让我们看一个例子。
class ModelA(models.Model):
pass
class ModelB(models.Model):
a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
# Forward ForeignKey relationship
ModelB.objects.select_related('a').all()
# Reverse ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all()
select_related通过多表连接协会查询一次获得所有数据,并通过减少数据库查询次数提高性能。它使用SQL的JOIN语句,通过减少SQL查询的次数来优化和提高性能。后者是通过JOIN语句来解决SQL查询中的问题。但是,对于多对多的关系,使用SQL语句来解决是不明智的,因为通过JOIN得到的表会很长,这将导致SQL语句的运行时间和内存占用增加。解决prefetch_related()的方法是分别查询每个表,然后用Python来处理它们之间的关系!
这里有一些例子。
Models.py的内容如下。
from django.db import models
class Province(models.Model):
name = models.CharField(max_length = 10)
def __unicode__(self):
return self.name
class City(models.Model):
name = models.CharField(max_length = 5)
province = models.ForeignKey(Province)
def __unicode__(self):
return self.name
class Person(models.Model):
firstname = models.CharField(max_length = 10)
lastname = models.CharField(max_length = 10)
visitation = models.ManyToManyField(City, related_name = "visitor")
hometown = models.ForeignKey(City, related_name = "birth")
living = models.ForeignKey(City, related_name = "citizen")
def __unicode__(self):
return self.firstname + self.lastname
select_related –
我们使用select_related()函数。
>>> citys = City.objects.select_related().all()
>>> for c in citys:
... print c.province
...
只有一个SQL查询,这显然大大减少了SQL查询的数量。
SELECT `Optimize_city`.`id`, `Optimize_city`.`name`,
`Optimize_city`.`province_id`, `Optimize_province`.`id`, `Optimize_province`.`name`
FROM`Optimize_city`
INNER JOIN `Optimize_province` ON
(`Optimize_city`.`province_id`=`Optimize_province`.`id`);
prefetch_related –
这里我们可以看到,Django使用了INNER JOIN。我想明确一点,Optimize是我们应用程序的一个名称。如果我们想得到湖北的所有城市名称,我们可以这样做。
> HB=Province.objects.prefetch_related('city_set').get(name__iexact=u"Hubei Province")
>>> for city in hb.city_set.all():
... city.name
...
触发的SQL查询。
SELECT `Optimize_province`.`id`, `Optimize_province`.`name`
FROM `Optimize_province`
WHERE `Optimize_province', `name `LIKE'Hubei Province';
SELECT `Optimize_city`.`id`, `Optimize_city`.`name`, `Optimize_city`.`province_id`
FROM `Optimize_city`
WHERE `Optimize_city`.`province_id` IN (1);
我们可以看到,预取是使用IN语句实现的。这样,当QuerySet中有太多的对象时,根据数据库的特点,可能会出现性能问题。