本文转载自https://www.douban.com/note/533925129/
版权归作者所有,任何形式转载请联系作者。
作者:petanne(来自豆瓣)
来源:https://www.douban.com/note/533925129/
补充:django1.5版本之后已经将select_related的**kwargs即depth删除了,递归机制与之前相同,即fields不传参数的时候默认递归查询related的所有字段,但django在源码里写死了最大递归深度depth为5,此前为无限。源码变为select_related(*fields)。查询过程与之前版本相同。
==============================原内容===================================
源码说明:
Returns a new QuerySet instance that will select related objects. # 默认的related objects为ForeignKey 和 OneToOneField
If fields are specified, they must be ForeignKey fields and only those related objects are included in the selection.
select_related(*fields, **kwargs)
此处kwargs只接收depth一个参数,传其它值抛异常,且fields和depth同时只能使用一个,否则抛异常。
假设有一个员工表,结构如下
Class Employee(models.Model):
dept = models.ForeignKey(Department, verbose_name=u'部门')
level = models.ForeignKey(Level, verbose_name=u'薪资级别', blank=True, null=True)
>>employee = Employee.objects.get(id=1)
>>dept = employee.dept # 又一次查询了数据库
>>level = employee.level # 又一次查询了数据库
>>employee = Employee.objects.select_related('dept').get(id=1)
>>dept = employee.dept # 没查询数据库
>>level = employee.level # 又一次查询了数据库
>>employee = Employee.objects.select_related('dept__name').get(id=1)
>>name = employee.dept.name # 没查询数据库
>>employee = Employee.objects.select_related('dept', 'level').get(id=1)
>>dept = employee.dept # 没查询数据库
>>level = employee.level # 没查询数据库
>>employee = Employee.objects.select_related(depth=1).get(id=1)
>>dept = employee.dept # 没查询数据库,性能与上面写法相同
>>level = employee.level # 没查询数据库,性能与上面写法相同
>>employee = Employee.objects.select_related().get(id=1)
>>dept = employee.dept # 没查询数据库,性能小于等于上面写法
>>level = employee.level # 没查询数据库,性能小于等于上面写法
注意!select_related不传任何参数的时候,默认会查询所有的关联对象,且会递归INNER JOIN所有的非空外键!!!如果Department表里有一个非空外键Region,那么会同时查询Region.id, Region.name, Region.other等!假如Region表又有一个非空外键Country,那么依然会全部INNER JOIN出来,性能消耗会呈指数型增长!若Department表的Region外键可空,那么只会查询到Region.id,不会进行递归。
对此,select_related提供了关键字参数depth,默认为0,即递归直到最后一个对象没有非空外键为止。depth为1时只递归一级外键,以此类推。
select_related的字段写法支持ORM语法,比如select_related('dept__name'),看起来像是只多select了Department的name属性,但是实际和select_related('dept')一样会将dept的所有属性查询出来,亲测目前django1.9版本是这个情况。
select_related('dept')与select_related(depth=1)效果完全一样,不论外键Region可空或非空,都只查询到Department.Region_id就截止了。所以使用select_related最好至少传一种参数!至少传一种参数!至少传一种参数!