以前看django文档时没有认真看这个函数,和当时看values一样都是一眼带过,没有真正弄懂这个函数是干用的,以至于我在使用django时也正抱怨,为什么django在取出一个对象时没把这个对象关联的外键对象也取出来,反而在用到这个外键的内容时再到数据库中去取,这样岂不是要两次连接数据库么?如果我一次取出多个对象,循环在界面上展示出来,它就要成倍的连接数据库,框架这样处理就太傻了吧,看到今天这个函数select_related,我算明白了,原来是否取出外键的值,控制权在自己手中,是我错怪django了。

看看网友提供的解释说明:

Django获取数据实体的时候,返回的对象一个实体或多个实体,也就是QuerySet,它是Django专有的东西,具体的理解,它是类似Python的字典的东西,但它并不实现字典的所有方法。今天讲解的是它其中的一个函数select_related()。


select_related()使用的场景,是获取实体的同时,能快速获取到它对应的外键关系实体。如果获取实体时没有使用select_related(),要引用该实体的外键关系,那么就会重新连接数据库去获取对应实体了。比如 作者Author 博客Blog 两个表,他们的关系 是一个作者Author有多个博客blog,;当我们想获取到一个作者的同时 直接引用该作者的所有博客时,常规做法是会进行两次数据库连接的,但使用了select_related()后,情况就有所变化了,它第一次连接数据库获取该作者的时候,该作者的所有博客被获取到了。
比如:
author=Author.objects.select_related().get(pk=1)  #获取id是1的作者; 获取该博客的同时已经获取到该作者的所有博客
blogs=author.blog #获取id是1作者的所有博客blogs  ,这时并不需要连接数据库获取那些博客了。

不使用select_related()时:
author=Author.objects.get(pk=1)  #获取id是1的作者;仅仅获取该作者的实体
blogs=author.blog #获取id是1作者的所有博客blogs  ,但需要连接数据库获取该作者的那些博客。

select_related()提供了一个备选参数depth,执行获取对象关联实体的深度。如 select_related(depth=2),这时不仅仅获取到相关的博客,还能获取到博客的关联实体,比如博客的类别(另一个和blog表相对应的表)之类的。如果不指定depth,默认情况将会获取到该作者的所有关联对象的 包括关联对象的子关联对象。由此可以想象,使用它有利有弊,当自己需要对象相关关联对象的时候,可以使用它,只是这样第一次查询的时候,将进行不少表的操作(要看表关联复杂程度)。

所以当我们获取对象需要它相关联的对象时,就请用select_related()吧,如果该对象关联的表很多,请指定depth的值,避免资源的浪费;反之避免select_related()。

有网友还反映,select_related函数好像只能取ManyToOne的关系, 不能取OneToMany关系,我先试试再说。

发现新情况:

  1. 无法指定外键对象的字段;

  2. 无法和values函数共存;

  3. select_related()函数中可以指定选出哪些字段,并非引用中说的只有一个入参depth;并且如果指定了字段就可以则默认depth=1。

    可以参考select_related()函数定义:

    def select_related(self, *fields, **kwargs):
        """
        Returns a new QuerySet instance that will select related objects.
     
        If fields are specified, they must be ForeignKey fields and only those
        related objects are included in the selection.
        """
        depth = kwargs.pop('depth', 0)
        if kwargs:
            raise TypeError('Unexpected keyword arguments to select_related: %s'
                    % (kwargs.keys(),))
        obj = self._clone()
        if fields:
            if depth:
                raise TypeError('Cannot pass both "depth" and fields to select_related()')
            obj.query.add_select_related(fields)
        else:
            obj.query.select_related = True
        if depth:
            obj.query.max_depth = depth
        return obj


  4. 可以考虑如果用select_related()函数来解决外键字段在界面上作为select标签时没有将option值一次取出的问题了。