前面豆子已经陆陆续续地学习了在Django中如何操作数据库
单表的基本操作 http://beanxyz.blog.51cto.com/5570417/1945887
常见字段的使用 http://beanxyz.blog.51cto.com/5570417/1945909
最基本的查询方式 http://beanxyz.blog.51cto.com/5570417/1950806
一对多的基本操作和实例 http://beanxyz.blog.51cto.com/5570417/1946602
多对多的基本操作和实例 http://beanxyz.blog.51cto.com/5570417/1952243
下面补充一些高级操作。
条件的过滤
下面是常见的条件设置,除了可以基本的filter之外,我们还有大量的条件语句可以使用。
查询数据库获取的QuerySet类型,对于这个类型我们类似Jquery一样使用链式编程,可以无限制的通过.来添加新的条件来过滤,可以看见大部分条件都是通过双下划线__来实现的
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# 获取个数
# models.Tb1.objects.filter(name='seven').count()
# 大于,小于
# models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
# models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
# in
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
# isnull
# Entry.objects.filter(pub_date__isnull=True)
# contains
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven")
# range
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
# 其他类似
# startswith,istartswith, endswith, iendswith,
# order by
# models.Tb1.objects.filter(name='seven').order_by('id') # asc
# models.Tb1.objects.filter(name='seven').order_by('-id') # desc
# group by
# from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
# limit 、offset
# models.Tb1.objects.all()[10:20]
# regex正则匹配,iregex 不区分大小写
# Entry.objects.get(title__regex=r'^(An?|The) +')
# Entry.objects.get(title__iregex=r'^(an?|the) +')
# date
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
# year
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)
# month
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)
# day
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)
# week_day
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)
# hour
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12)
# minute
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29)
# second
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)
|
上面这些方法可以实现大部分常见的简单查询过滤。有的时候,我们需要实现一些更复杂的查询语句,上面的语句就不够用了,这个时候可以通过extra来扩展。例如,主要看看select和where的自定义
1
2
3
4
5
6
|
# extra
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
|
反向查询
之前的博文里面,我们都是通过正向查找外键或者中间表来获取另外一个表的信息;如果希望倒过来,也是通过双下划线,比如 表名__字段 这种形式来实现
实例:
3张表,分别是单表,1对多和多对多的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#业务线
class
Business(models.Model):
# id
caption
=
models.CharField(max_length
=
32
)
#主机
class
Host(models.Model):
nid
=
models.AutoField(primary_key
=
True
)
hostname
=
models.CharField(max_length
=
32
,db_index
=
True
)
ip
=
models.GenericIPAddressField(protocol
=
"ipv4"
,db_index
=
True
)
port
=
models.IntegerField()
b
=
models.ForeignKey(to
=
"Business"
, to_field
=
'id'
)
#程序
class
Application(models.Model):
name
=
models.CharField(max_length
=
32
,unique
=
True
)
r
=
models.ManyToManyField(
"Host"
)
|
视图函数
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
|
def
tt(request):
#1对多正向查找
print
(
'1对多正向查找'
.center(
40
,
'-'
))
obj
=
models.Host.objects.
filter
(nid__gt
=
1
)
print
(obj[
0
].nid,obj[
0
].hostname,obj[
0
].b.caption)
#一些过滤条件
print
(
'过滤条件'
.center(
40
,
'-'
))
obj
=
models.Business.objects.
filter
(caption__contains
=
'SALE'
).first()
print
(obj.
id
,obj.caption)
obj
=
models.Business.objects.
all
().values(
'id'
,
'caption'
)
print
(obj, obj.order_by(
'id'
).reverse())
obj
=
models.Application.objects.
filter
(name__exact
=
'SQL Server'
).values(
'name'
,
'r__hostname'
,
'r__nid'
)
print
(obj)
#1对多反向查找
print
(
'1对多反向查找'
.center(
40
,
'-'
))
obj
=
models.Business.objects.
all
().values(
'id'
,
'caption'
,
'host__ip'
,
'host__hostname'
)
print
(obj[
0
])
#多对多正向查找
print
(
'多对多正向查找'
.center(
40
,
'-'
))
obj
=
models.Application.objects.
all
().first()
print
(obj.name, obj.r.
all
()[
0
].hostname)
#多对多反向查询
print
(
'多对多反向查找'
.center(
40
,
'-'
))
obj
=
models.Host.objects.
all
().
filter
(nid__gt
=
1
).values(
'nid'
,
'application__name'
).first()
print
(obj)
return
HttpResponse(
'ok'
)
|
执行结果
1
2
3
4
5
6
7
8
9
10
11
12
|
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1
对多正向查找
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
183
SYDMGM01 SALE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
过滤条件
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
5
SALE
<QuerySet [{
'id'
:
5
,
'caption'
:
'SALE'
}, {
'id'
:
19
,
'caption'
:
'IT'
}, {
'id'
:
20
,
'caption'
:
'HR'
}]> <QuerySet [{
'id'
:
20
,
'caption'
:
'HR'
}, {
'id'
:
19
,
'caption'
:
'IT'
}, {
'id'
:
5
,
'caption'
:
'SALE'
}]>
<QuerySet [{
'name'
:
'SQL Server'
,
'r__hostname'
:
'SYDMGM01'
,
'r__nid'
:
183
}, {
'name'
:
'SQL Server'
,
'r__hostname'
:
'SYDAV01'
,
'r__nid'
:
190
}, {
'name'
:
'SQL Server'
,
'r__hostname'
:
'SYDMGM02'
,
'r__nid'
:
191
}]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
1
对多反向查找
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{
'id'
:
5
,
'caption'
:
'SALE'
,
'host__ip'
:
'10.2.1.1'
,
'host__hostname'
:
'SYDMGM01'
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
多对多正向查找
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AA SYDMGM01
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
多对多反向查找
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{
'nid'
:
183
,
'application__name'
:
'AA'
}
|
性能
假设我们有一个Use表通过外键ut绑定了一个UserType表
默认情况下,如果我们直接使用下面代码,如果uses获取了10行数据,那么数据库实际上查询11次,对user查询一次,然后在for循环里面对usertype查询10次;这样效率很低
1
2
3
4
|
users
=
models.User.objects.
all
()
for
row
in
users:
print
(row.user,row.pwd,row.ut_id)
print
(row.ut.name)
|
我们可以通过select_related进行优化,这样第一次查询的时候就进行一个跨表查询,获取指定外键的所有数据
1
2
3
4
|
users
=
models.User.objects.
all
().select_related(
'ut'
)
for
row
in
users:
print
(row.user,row.pwd,row.ut_id)
print
(row.ut.name)
|
如果数据比较多,外键也多,那么速度可能还会比较慢,比较跨表查询的效率比较低,那么进一步的我们可以通过prefetch_related优化。他的基本原理是进行多次单表查询;比如第一次查询User表,然后第二次查询外键关联的表,然后把所有数据都放在内存里面,这样访问的速度就会快很多了。
1
2
3
4
5
6
7
8
|
users
=
models.User.objects.
filter
(id__gt
=
30
).prefetch_related(
'ut'
,
'tu'
)
# select * from users where id > 30
# 获取上一步骤中所有的ut_id=[1,2]
# select * from user_type where id in [1,2]
# select * from user_type where id in [1,2]
for
row
in
users:
print
(row.user,row.pwd,row.ut_id)
print
(row.ut.name)
|