详解Django的 prefetch_related() 函数:对关联对象进行预取

  • Post category:Python

Django的prefetch_related()函数是用于优化ORM数据库查询的函数,用于在获取QuerySet集合时通过一部查询获取一些相关联的数据。

该函数使用场景为在一对多或者多对多的场景下,查询主表数据时,预先自动查询对应的外键关联表数据,并将查询结果一起返回,可以的提高数据检索的效率,减少查询数据库的次数。

prefetch_related()函数的使用方法如下:

从单个关联属性开始,该属性会在后续查询中被提前预存。
Model.objects.prefetch_related('related_attr').get(pk=1)

# 查询所有实例时预存,这样将能够减少一次查询
qs = Model.objects.prefetch_related('related_attr')

# 在'prefetch_related'调用链上添加额外的关系
qs = qs.prefetch_related('other_attr')

# 在与其他时间消耗较长的查询连通中使用
Model.objects.prefetch_related(Prefetch('categories', queryset=Categories.objects.filter(is_published=True)))

其中,’related_attr’是与Model模型相关联的外键属性、正向多对多属性或反向多对多属性。’other_attr’是与’prefetch_related’方法调用已有关联的实例中某个属性相关联的外键属性、正向多对多属性或反向多对多属性。

下面给出两个具体实例,以说明prefetch_related()函数的作用。

1、查询文章列表时,预处理并提取出文章的标签信息

# models.py
class Article(models.Model):
    title = models.CharField(max_length=50)
    content = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)

class Tag(models.Model):
    name = models.CharField(max_length=20)
    articles = models.ManyToManyField(Article)

# views.py
def article(request):
    articles = Article.objects.prefetch_related('tag_set').order_by('-pub_date')
    return render(request, 'article_list.html', {'articles': articles})

# article_list.html
{% for article in articles %}
    <h2>{{ article.title }}</h2>
    <p>{{ article.content }}</p>
    <ul>
    {% for tag in article.tag_set.all %}
        <li>{{ tag.name }}</li>
    {% endfor %}
    </ul>
{% endfor %}

可以看到,在视图函数中,我们使用 prefetch_related() 函数预先提前查询与文章相关联的标签信息,然后在模板文件中使用 article.tag_set.all 获取文章的标签信息,避免了代码中多次查询数据库信息的问题,提高了查询效率。

2、查询城市列表时,预处理并提取出城市所在的省份信息

# models.py
class Province(models.Model):
    name = models.CharField(max_length=20)

class City(models.Model):
    name = models.CharField(max_length=20)
    province = models.ForeignKey(Province, on_delete=models.CASCADE)

# views.py
def city(request):
    provinces = Province.objects.prefetch_related('city_set').order_by('id')
    return render(request, 'city_list.html', {'provinces': provinces})

# city_list.html
{% for province in provinces %}
    <h2>{{ province.name }}</h2>
    <ul>
    {% for city in province.city_set.all %}
        <li>{{ city.name }}</li>
    {% endfor %}
    </ul>
{% endfor %}

在视图函数中,我们使用 prefetch_related() 函数预先查询与省份相关联的城市信息,然后在模板中使用province.city_set.all获取与该省份相关联的所有城市信息,避免了多次查询数据库的问题,提高了查询效率。

综上,prefetch_related()函数提供了ORM数据检索的整洁调用方式,能够减少代码中的嵌套查询和提高数据访问效率。