开发者社区> 余二五> 正文

Django 数据库高级操作-过滤,反向查询,性能

简介:
+关注继续查看

前面豆子已经陆陆续续地学习了在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()
    = models.ForeignKey(to="Business", to_field='id')
#程序
class Application(models.Model):
    name = models.CharField(max_length=32,unique=True)
    = 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)



参考资料:http://www.cnblogs.com/wupeiqi/articles/5246483.html






本文转自 beanxyz 51CTO博客,原文链接:http://blog.51cto.com/beanxyz/1967344,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【第17个代码模型】使用 PreparedStatement 操作数据库(PrepareStatement 查询案例)|学习笔记
快速学习 【第17个代码模型】使用 PreparedStatement 操作数据库(PrepareStatement 查询案例)
52 0
Django学习笔记----数据库操作实例
上上篇>Django学习笔记----环境搭建基于Windows 上一篇>Django学习笔记----快速入门 修改settings.py配置 在环境搭建篇, 我们已经安装了mysql-client包 安装好后, 在settings.
1281 0
Django-ORM数据库操作
Django框架功能齐全自带数据库操作功能,由于工作中设计巨量的api接口,需要一个很好的web后端服务框架,Django给了莫大的帮助。本文主要介绍Django的ORM框架
4098 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
13862 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
20690 0
+关注
20381
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载