问题描述:
对于Django中的“N+1”问题,如:
posts = Post.objects.all()
for post in posts: # 产生1次数据库查询所有的post
print(post.owner.username) # 产生1次额外的外键数据库查询
print(post.category.name) # 产生1次额外的外键数据库查询
这样会导致线性的SQL查询,如果对象数量N太多,每个对象中有k个外键字段的话,就会导致Nk+1次SQL查询。在本例中,因为有N个post对象,每个对象有2个外键,就导致了N2+1次SQL查询,这个1次是为了获取所有的post。
问题解决:
1、使用select_related接口,针对一对一外键。
# Django 1.7版本之前,只能使用
Post.objects.all().select_related('category', 'owner')
# Django 1.7版本之后,也可以使用链式操作(即上下两种方式都可以):
Post.objects.all().select_related('category').select_related('owner')
for post in posts: # 产生1次数据库查询所有的post,category和owner也会一次性查询出来
print(post.owner.username)
print(post.category.name)
所以上面的代码,总共只会产生一次sql查询。
2、针对多对多外键的数据,可以使用prefetch_related接口来解决:
Post.objects.all().prefetch_related('tag')
for post in posts: # 产生两次sql查询,分别查询所有的post和对应的tag
print(post.tag.all())
所以上面的代码,总共只会产生2次sql查询。