来源:http://blog.jobbole.com/75435/
这是本系列的最后一篇,主要是select_related() 和 prefetch_related() 的最佳实践。
第一篇在这里 讲例子和select_related()
第二篇在这里 讲prefetch_related()
1
2
3
4
5
|
>>> hb
=
Province.objects.get(name__iexact
=
u
"湖北省"
)
>>> people
=
[]
>>>
for
city
in
hb.city_set.
all
():
... people.extend(city.birth.
all
())
...
|
1
2
3
4
5
|
>>> hb
=
Province.objects.prefetch_related(
"city_set__birth"
).objects.get(name__iexact
=
u
"湖北省"
)
>>> people
=
[]
>>>
for
city
in
hb.city_set.
all
():
... people.extend(city.birth.
all
())
...
|
1
2
3
4
5
6
7
8
9
10
11
12
|
SELECT
`QSOptimize_province`.`id`, `QSOptimize_province`.`
name
`
FROM
`QSOptimize_province`
WHERE
`QSOptimize_province`.`
name
`
LIKE
'湖北省'
;
SELECT
`QSOptimize_city`.`id`, `QSOptimize_city`.`
name
`, `QSOptimize_city`.`province_id`
FROM
`QSOptimize_city`
WHERE
`QSOptimize_city`.`province_id`
IN
(1);
SELECT
`QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`,
`QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`
FROM
`QSOptimize_person`
WHERE
`QSOptimize_person`.`hometown_id`
IN
(1, 3);
|
1
|
>>> people
=
list
(Person.objects.select_related(
"hometown__province"
).
filter
(hometown__province__name__iexact
=
u
"湖北省"
))
|
1
2
3
4
5
6
7
|
SELECT
`QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`,
`QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`, `QSOptimize_city`.`id`,
`QSOptimize_city`.`
name
`, `QSOptimize_city`.`province_id`, `QSOptimize_province`.`id`, `QSOptimize_province`.`
name
`
FROM
`QSOptimize_person`
INNER
JOIN
`QSOptimize_city`
ON
(`QSOptimize_person`.`hometown_id` = `QSOptimize_city`.`id`)
INNER
JOIN
`QSOptimize_province`
ON
(`QSOptimize_city`.`province_id` = `QSOptimize_province`.`id`)
WHERE
`QSOptimize_province`.`
name
`
LIKE
'湖北省'
;
|
1
2
3
4
5
6
7
8
|
+
----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
| id | firstname | lastname | hometown_id | living_id | id |
name
| province_id | id |
name
|
+
----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
| 1 | 张 | 三 | 3 | 1 | 3 | 十堰市 | 1 | 1 | 湖北省 |
| 2 | 李 | 四 | 1 | 3 | 1 | 武汉市 | 1 | 1 | 湖北省 |
| 3 | 王 | 麻子 | 3 | 2 | 3 | 十堰市 | 1 | 1 | 湖北省 |
+
----+-----------+----------+-------------+-----------+----+--------+-------------+----+--------+
3
rows
in
set
(0.00 sec)
|
1
2
3
4
5
6
|
class
Order(models.Model):
customer
=
models.ForeignKey(Person)
orderinfo
=
models.CharField(max_length
=
50
)
time
=
models.DateTimeField(auto_now_add
=
True
)
def
__unicode__(
self
):
return
self
.orderinfo
|
1
2
3
4
|
>>> plist
=
Order.objects.prefetch_related(
'customer__visitation__province'
).get(
id
=
1
)
>>>
for
city
in
plist.customer.visitation.
all
():
...
print
city.province.name
...
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
SELECT
`QSOptimize_order`.`id`, `QSOptimize_order`.`customer_id`, `QSOptimize_order`.`orderinfo`, `QSOptimize_order`.`
time
`
FROM
`QSOptimize_order`
WHERE
`QSOptimize_order`.`id` = 1 ;
SELECT
`QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`
FROM
`QSOptimize_person`
WHERE
`QSOptimize_person`.`id`
IN
(1);
SELECT
(`QSOptimize_person_visitation`.`person_id`)
AS
`_prefetch_related_val`, `QSOptimize_city`.`id`,
`QSOptimize_city`.`
name
`, `QSOptimize_city`.`province_id`
FROM
`QSOptimize_city`
INNER
JOIN
`QSOptimize_person_visitation`
ON
(`QSOptimize_city`.`id` = `QSOptimize_person_visitation`.`city_id`)
WHERE
`QSOptimize_person_visitation`.`person_id`
IN
(1);
SELECT
`QSOptimize_province`.`id`, `QSOptimize_province`.`
name
`
FROM
`QSOptimize_province`
WHERE
`QSOptimize_province`.`id`
IN
(1, 2);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
+----+-------------+---------------+---------------------+
| id | customer_id | orderinfo | time |
+----+-------------+---------------+---------------------+
| 1 | 1 | Info of Order | 2014-08-10 17:05:48 |
+----+-------------+---------------+---------------------+
1 row in set (0.00 sec)
+----+-----------+----------+-------------+-----------+
| id | firstname | lastname | hometown_id | living_id |
+----+-----------+----------+-------------+-----------+
| 1 | 张 | 三 | 3 | 1 |
+----+-----------+----------+-------------+-----------+
1 row in set (0.00 sec)
+-----------------------+----+--------+-------------+
| _prefetch_related_val | id | name | province_id |
+-----------------------+----+--------+-------------+
| 1 | 1 | 武汉市 | 1 |
| 1 | 2 | 广州市 | 2 |
| 1 | 3 | 十堰市 | 1 |
+-----------------------+----+--------+-------------+
3 rows in set (0.00 sec)
+----+--------+
| id | name |
+----+--------+
| 1 | 湖北省 |
| 2 | 广东省 |
+----+--------+
2 rows in set (0.00 sec)
|
更好的办法是先调用一次select_related()再调用prefetch_related(),最后再select_related()后面的表
1
2
3
4
|
>>> plist
=
Order.objects.select_related(
'customer'
).prefetch_related(
'customer__visitation__province'
).get(
id
=
1
)
>>>
for
city
in
plist.customer.visitation.
all
():
...
print
city.province.name
...
|
这样只会有3次SQL查询,Django会先做select_related,之后prefetch_related的时候会利用之前缓存的数据,从而避免了1次额外的SQL查询:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
SELECT
`QSOptimize_order`.`id`, `QSOptimize_order`.`customer_id`, `QSOptimize_order`.`orderinfo`,
`QSOptimize_order`.`
time
`, `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`,
`QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`
FROM
`QSOptimize_order`
INNER
JOIN
`QSOptimize_person`
ON
(`QSOptimize_order`.`customer_id` = `QSOptimize_person`.`id`)
WHERE
`QSOptimize_order`.`id` = 1 ;
SELECT
(`QSOptimize_person_visitation`.`person_id`)
AS
`_prefetch_related_val`, `QSOptimize_city`.`id`,
`QSOptimize_city`.`
name
`, `QSOptimize_city`.`province_id`
FROM
`QSOptimize_city`
INNER
JOIN
`QSOptimize_person_visitation`
ON
(`QSOptimize_city`.`id` = `QSOptimize_person_visitation`.`city_id`)
WHERE
`QSOptimize_person_visitation`.`person_id`
IN
(1);
SELECT
`QSOptimize_province`.`id`, `QSOptimize_province`.`
name
`
FROM
`QSOptimize_province`
WHERE
`QSOptimize_province`.`id`
IN
(1, 2);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
+----+-------------+---------------+---------------------+----+-----------+----------+-------------+-----------+
| id | customer_id | orderinfo | time | id | firstname | lastname | hometown_id | living_id |
+----+-------------+---------------+---------------------+----+-----------+----------+-------------+-----------+
| 1 | 1 | Info of Order | 2014-08-10 17:05:48 | 1 | 张 | 三 | 3 | 1 |
+----+-------------+---------------+---------------------+----+-----------+----------+-------------+-----------+
1 row in set (0.00 sec)
+-----------------------+----+--------+-------------+
| _prefetch_related_val | id | name | province_id |
+-----------------------+----+--------+-------------+
| 1 | 1 | 武汉市 | 1 |
| 1 | 2 | 广州市 | 2 |
| 1 | 3 | 十堰市 | 1 |
+-----------------------+----+--------+-------------+
3 rows in set (0.00 sec)
+----+--------+
| id | name |
+----+--------+
| 1 | 湖北省 |
| 2 | 广东省 |
+----+--------+
2 rows in set (0.00 sec)
|
值得注意的是,可以在调用prefetch_related之前调用select_related,并且Django会按照你想的去做:先select_related,然后利用缓存到的数据prefetch_related。然而一旦prefetch_related已经调用,select_related将不起作用。