博客园 首页 博问 闪存 新随笔 订阅 管理ORM:(在django中,根据代码中的类自动生成数据库的表也叫--code first)

ORM:Object Relational Mapping(关系对象映射)

我们写的类表示数据库中的表

我们根据这个类创建的对象是数据库表里的一行数据

obj.id obj.name.....就是数据库一行数据中的一部分数据

ORM--First:

我们在学习django中的orm的时候,我们可以把一对多,多对多,分为正向和反向查找两种方式。

1234567classUserType(models.Model):caption=models.CharField(max_length=32)classUserInfo(models.Model):username=models.CharField(max_length=32)age=models.IntegerField()user_type=models.ForeignKey('UserType')#外键

正向查找:ForeignKey在 UserInfo表中,如果从UserInfo表开始向其他的表进行查询,这个就是正向操作,反之如果从UserType表去查询其他的表这个就是反向操作。

马上就要开始我们的orm查询之旅!!!

建表+配置url+views中写相应的函数

models.py(在django中仅且只能在这里写数据库的相关类)

1234567classUserType(models.Model):caption=models.CharField(max_length=32)classUserInfo(models.Model):username=models.CharField(max_length=32)age=models.IntegerField()user_type=models.ForeignKey('UserType')

这里我们使用sqlite3数据库,在settings中使用默认设置就可以了

url.py中的配置

123456789fromdjango.conf.urlsimporturlfromdjango.contribimportadminfromapp01importviewsurlpatterns=[url(r'^admin/', admin.site.urls),url(r'^user_type',views.user_type),url(r'^user_info',views.user_info),]

views.py先不进行任何操作,我们先保证正常访问已经设置的url

1234567fromdjango.shortcutsimportrender,HttpResponsedefuser_type(req):returnHttpResponse("ok")defuser_info(req):returnHttpResponse("ok")

先在表中插入几个数据用于测试:

usertype表

userinfo表数据插入:

所以我们在创建UserType数据的时候有两种方法:第一种方法是直接根据这个字段进行添加数据!给user_type 加 '_id'

1234defuser_info(request):dic={'username':'mosson','age':18,'user_type_id':1}models.UserInfo.objects.create(**dic)returnHttpResponse('OK')

或者通过对象添加

1234567#先获取组的对象usertype=models.UserType.objects.fiter(id=2)#添加的时候直接添加对象就可以models.UserInfo.objects.create(username='seven',age=18,user_type=usertype)#写成一行也行models.UserInfo.objects.create(username='lile',age=18,user_type=models.UserType.objects.filter(id=1))

django的get方法是从数据库的取得一个匹配的结果,返回一个对象,如果记录不存在的话,它会报错。
django的filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。

ORM的一对多:

我们在设计表结构的时候什么时候使用一对多呢?

比如我们在建立用户的时候有个菜单让我们选择用户类型的时候,使用一对多!!

1、一对多的正向查找:

正向查:ForeignKey在UserInfo表里,如果根据UserInfo这张表去查询这两张关联的表的合起来的内容就是正向查

反向查:ForeignKey不在UserType里,如果根据UserType这张表去查询这两张关联的表的合起来的内容就是反向查

正向查-demo1--查询所有用户为COO 的用户

在django中外键就相当于简单的使用__连表,在外键那个对象中封装了user_type表中的所有字段

我们要查询所有用户为CEO的用户,我们是不是的根据UserType这张表去查,如果是跨表查询使用“双下划线” + 属性

1234567fromapp01importmodelsdefindex(req):ret=models.UserInfo.objects.filter(user_type__caption='COO')print(ret)foriteminret:print(item,item.username,item.age,item.user_type.caption)returnHttpResponse("OK")

查询结果:

反向查询:--

我们可以根据下面的命令,取出一个用户组,那么对于这个用户组他有多少个用户呢?

1models.UserType.objects.get(id=1)1234567891011obj=models.UserType.objects.get(id=1)obj.caption====得到在UserType表中id为1对应的captionobj.id======得到在UserType表中id为1obj.userinfo_set#理解为一种能力,可以获取所有当前这个用户类型的用户/或者这个用户类型的多个用户obj.userinfo_set.all()#获取所有用户类型为COO的用户obj.userinfo_set.filter(username='tim')#获取用户类型为COO的并且用户为tim的用户'''这.userinfo_set.相当于什么?它相当于 models.UserInfo.objects.filter(user_type=obj)'''

反向查询实例:查询COO用户下的所有的用户名

123ret=models.UserType.objects.filter(caption='COO').values('userinfo__username')foriteminret:print(item,type(item))

方法二、

1234ret=models.UserType.objects.filter(caption='COO').first()foriteminret.userinfo_set.all():print(item.username)

总结:

  正向查找:

    filter(跨表的时候,应该是对象__跨表的字段)

    获取这个值的时候,拿到了一行数据的时候 line.对象.跨表的字段

  反向查找:

    filter(关联这个表的表明) 自动创建和表明相同的对象,通过这个对象__跨表的字段

    line.自动创建和表明相同的对象_set.方法

ORM多对多 系统生成第三张表:

多对多和一对多没有任何关系

models.py

12345678classHost(models.Model):hostname=models.CharField(max_length=32)port=models.IntegerField()classHostAdmin(models.Model):username=models.CharField(max_length=32)email=models.CharField(max_length=32)host=models.ManyToManyField('Host')

当我们在host表里和hostadmin表里添加数据时和第三章关系表没有任何关系,当我们这样去建立表时,第三张表里面的列就已经固定了,分别是这两个表的id

给主机表添加数据:

1234567891011defindex(req):#主机数据models.Host.objects.create(hostname='host1.test.com', port=80)models.Host.objects.create(hostname='host2.test.com', port=80)models.Host.objects.create(hostname='host3.test.com', port=80)models.Host.objects.create(hostname='host4.test.com', port=80)#用户数据models.HostAdmin.objects.create(username='alex', email='alex@qq.com')models.HostAdmin.objects.create(username='mosson', email='mosson@qq.com')models.HostAdmin.objects.create(username='aliven', email='aliven@qq.com')models.HostAdmin.objects.create(username='wusir', email='wusir@qq.com')

数据中的效果:

空的关系表

多对多正向、反向添加数据

正向添加数据:

123456789101112131415161718defindex(request):#正向添加数据#找到用户dali这个admin_obj=models.HostAdmin.objects.get(username='dali')#找到主机host_list=models.Host.objects.filter(id__lt=3)#通过找到的dali的对象.add去添加数据admin_obj.host.add(*host_list)'''admin_obj 通过大力这个对象.add 去操作的主机,大力的ID为 2 主机ID为:(1,2)那就会生成这样的表:#2 1#2 2'''returnHttpResponse('OK')

反向添加数据:

1234567891011121314defindex(request):#反向添加数据#获取主机host_obj=models.Host.objects.get(id=3)#获取用户列表admin_list=models.HostAdmin.objects.filter(id__gt=1)#和一对多一样的道理host_obj.hostadmin_set.add(*admin_list)#host_obj = 3 管理员ID = 2 3 4#3 2#3 3#3 4returnHttpResponse('OK')ORM 多对多 自定义 第三张表123456789101112131415161718192021classHostInfo(models.Model):hostname=models.CharField(max_length=32)port=models.IntegerField()classUserMap(models.Model):username=models.CharField(max_length=32)email=models.CharField(max_length=32)#through告诉Django用那张表做关联host=models.ManyToManyField(HostInfo , through='HostRelation')classHostRelation(models.Model):host=models.ForeignKey('HostInfo')user=models.ForeignKey('UserMap')'''并且这里我们可以添加多个关系,比如在加一个字段usertype = models.ForeignKey('UserType')或者增加一个普通字段status = models.CharField(max_length=32)'''

现在咱们自己创建了第三张表了。现在我们已经会了两种方式创建第三张表了,当我们使用自定义创建的第三张表的时候,在去添加数据的时候!

就不能使用第一种方式对象添加了!

现在我们有第三张表了这个对象了,我们就不需要管另外两张表了,直接添加就行了! 0 0 !

添加主机和用户--

1234567891011defindex(req):models.HostInfo.objects.create(hostname='alex.test.com', port=80)models.HostInfo.objects.create(hostname='seven.test.com', port=80)models.HostInfo.objects.create(hostname='mosson.test.com', port=80)models.UserMap.objects.create(username='alex', email='alex@qq.com')models.UserMap.objects.create(username='seven', email='seven@qq.com')models.UserMap.objects.create(username='mosson', email='mosson@qq.com')returnHttpResponse('ok')

将数据插入到第三张表中---办法1:

插入单条数据

123456defindex(request):models.HostRelation.objects.create(user=models.UserMap.objects.get(id=1),host=models.HostInfo.objects.get(id=1))returnHttpResponse('OK')

多对多两种方式对比和查询

查询--方法一:

第一种方式都是基于表中的对象去找到第三张表! 通过间接的方式找到这张表的句柄!

123456#正向查admin_obj=models.HostAdmin.objects.get(id=1)admin_obj.host.all()#反相差host_obj=models.Host.objects.get(id=1)host_obj.hostadmin_set.all()

查询--方法二:

用第二种方法就没有正向和反向这么一说了,直接查即可!

1234relation_list=models.HostRelation.objects.all()foriteminrelation_list:#每一个item就是一个关系print(item.user.username)print( item.host.hostname)1234relation_list=models.HostRelation.objects.filter(user__username='mosson')foriteminrelation_list:#每一个item就是一个关系print( item.user.username)print(item.host.hostname)

通过第二种方式可以把所有的关系都找到,第一种方式可以把所有的关系表都找到吗?

第一种方式只能找到某一个人管理的机器,不能把有的对应关系找到!

select_related的作用:1234567classUserType(models.Model):caption=models.CharField(max_length=32)classUserInfo(models.Model):user_type=models.ForeignKey('UserType')#这个user_type是一个对象,对象里面封装了ID和captionusername=models.CharField(max_length=32)age=models.IntegerField()

select_related的作用,他就是用来优化查询的,没有他也可以,用它主要是用来优化ForeignKey

1234567defindex(request):ret=models.UserInfo.objects.all()#咱们看下他执行的什么SQL语句print( ret.query)'''SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age" FROM "app01_userinfo"'''

加上select_related是什么样子的

1234567891011121314defuser_info(request):ret=models.UserInfo.objects.all().select_related()#咱们看下他执行的什么SQL语句printret.querySELECT"app01_userinfo"."id","app01_userinfo"."user_type_id","app01_userinfo"."username","app01_userinfo"."age","app01_usertype"."id","app01_usertype"."caption"FROM"app01_userinfo"INNER JOIN"app01_usertype"ON ("app01_userinfo"."user_type_id"="app01_usertype"."id")

所以说select_related就是优化查询的!

ORM连表操作的梳理:

一、一对多创建

  1、创建数据

    通过对象创建

    或者通过对象字段_id创建

  2、查找

    正向查找

      在通过filter的时候跨表使用 双下划线 '__'

      在获取值得时候通过.跨表

    反向查找

      Django自动生成 表名_set

      其他操作和正向查找一样

二、多对对

  1、自动生成关系表

    间接的方式获取关系表,如果是正向的:一行数据的对象.ManyToMany字典就行 反向:一行数据的对象.表名_set

  2、自定义关系表(推荐)不管是添加、修改只对关系表操作就行

三、select_related

  用于优化查询,一次性将查询的表和ForiegnKey关联的表一次性加载到内存。

Django中的F和Q

F:用来批量修改数据(使用查询条件的值)

demo:比如我有个price这个列,我想让price自增10或者某一些自增10

12# from django.db.models import F# models.Tb1.objects.update(num=F('num')+10)

Q:用来做条件查询的

默认的django的查询只是且操作如下:

1models.UserInfo.objects.filter(username='mosson',age='18')

找到用户名为:mosson且age=18的数据

有没有这么一种情况:username=mosson或 username=wusir 或 username=alex 并且 age=18的需求?原生的查询是不支持的!所以就用到了Q~

123456789101112131415161718192021222324252627282930313233343536第一步:#生成一个搜索对象search_q=Q()#在生成两个搜索对象search2=Q()search3=Q()第二步:#标记search2中的搜索条件为 ‘ 或’ 查询search2.connector='OR'#把搜索条件加入到search2中search2.children.append(('字段名','字段内容'))search2.children.append(('字段名','字段内容'))search2.children.append(('字段名','字段内容'))search2.children.append(('字段名','字段内容'))#标记search3中的搜索条件为 ‘ 或’ 查询search3.connector='OR'#把搜索条件加入到search3中search3.children.append(('字段名','字段内容'))search3.children.append(('字段名','字段内容'))search3.children.append(('字段名','字段内容'))search3.children.append(('字段名','字段内容'))第三步:#把多个搜索条件进行合并search_q.add(search2,'AND')search_q.add(search3,'AND')第四步:#执行搜索models.HostInfo.objects.filter(search_q)实例:

在前端获取搜索条件的时候我把相同类型的搜索写成字典的形式{字段名:[条件结合列表]},这样我在查询的时候直接通过循环字典就可以把搜索条件分为不同的子条件!

123456789101112131415161718192021222324252627282930function SearchSubmit() {//清空当前列表$('#table-body').children().remove();//设置一个空字典SEARCH_DIC={};//找到所有的搜索框体var search_data=$('.inputs').find("input[is-condition='true']");//循环找到的内容$.each(search_data,function (index,data) {//获取搜索的类型/*这里需要注意:重点:::::这里和Django的Q可以进行耦合,在我们定义搜索的类型的时候可以直接写成我们要搜索的的'库中的字段或者条件都可以!!!'如下:<inputis-condition="true"type="text"placeholder="逗号分割多条件"class="form-control no-radius"name="hostname"/>*/var search_name=$(this).attr('name');//获取搜索的值var search_value=$(this).val();if(SEARCH_DIC.hasOwnProperty(search_name)){//判断是否有这个KEYSEARCH_DIC[search_name].push(search_value)}else{SEARCH_DIC[search_name]=[search_value]}} );//ajax请求结束$.get("{% url 'search_info' %}",{'search_list':JSON.stringify(SEARCH_DIC)},function(callback){$('#table-body').append(callback)});//搜索按钮结束

重点:

在前端我们定义input的name属性的时候,我们可以直接定义为数据库中的“字段名”,并且在Django的Q中支持跨表操作“双下划线”,所以我们在定义name的时候可以直接定义双下划线操作

1search2.children.append(('字段名'__'跨表字段名','跨表字段内容'))12345678910111213141516171819202122232425262728293031323334353637383940@login_authdefsearch_info(request):#获取用户请求的数据user_post=json.loads(request.GET['search_list'])printuser_post#生成搜索对象Serach_Q=Q()#循环字典并生成搜索条件集合fork,vinuser_post.items():#生成一个搜索结合q=Q()#生命集合中的搜索条件为'或'条件q.connector='OR'#循环字典中的value,value是前端传过来的条件集合foriinv:#在搜索条件集合中增加条件,条件为元组形式,k为字典中的key! key是字段名或者跨表字段名或者支持_gt等#i为字典中的vlaue中的元素,为条件#q.children.append((k,i))#没循环一次后后,吧他加入到总的搜索条件中Serach_Q.add(q,'AND')#使用总的搜索条件进行查询data=models.HostInfo.objects.filter(Serach_Q)#拼接字符串并返回html=[]foriindata:html.append(""+"<td>"+"<input type='checkbox' >"+""+"<td name='host_id'>"+'%s'%i.id+""+"<td name='host_name' edit='true'>"+i.hostname+""+"<td name='host_ip' edit='true'>"+i.hostip+""+"<td name='host_port' edit='true'>"+'%s'%i.hostport+""+"<td name='host_business' edit='true' edit-type='select' global-key='BUSINESS' select-val='"+'%s' %i.hostbusiness_id + "'>"+i.hostbusiness.hostbusiness+""+"<td name='host_status' edit='true' edit-type='select' global-key='STATUS' select-val='"+'%s' %i.hoststatus_id + "'>"+i.hoststatus.hoststatus+""+"")html=mark_safe("".join(html))returnHttpResponse(html)


分类:Django好文要顶关注我收藏该文想做土匪的书生
关注 - 12
粉丝 - 2100?上一篇:奇淫技巧第二季
?下一篇:Python线程
posted on2017-03-23 20:47想做土匪的书生阅读(371) 评论(0)编辑收藏刷新评论刷新页面返回顶部【推荐】50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
【免费】从零开始学编程,开发者专属实验平台免费实践!
【推荐】现在注册又拍云,首月可享 200G CDN流量,还可免费申请 SSL 证书
昵称:想做土匪的书生
园龄:1年1个月
粉丝:21
关注:12<2017年7月>日一二三四五六2526272829301234567891011121314151617181920212223242526272829303112345搜索随笔分类Django(5)Flask全系列(5)Git(1)Interviews(1)Linux系列(5)mysql&&sqlalchemy(8)python零散知识(2)py基础(5)py记忆的重点(2)rabbitmq(3)框架-----Python模块解密(2)奇技淫巧大全(4)前端html/css/jqurey(5)设计模式(1)算法(1)网络基础(1)小虫子scapy(1)随笔档案2017年7月 (1)2017年6月 (6)2017年5月 (1)2017年3月 (13)2017年2月 (2)2017年1月 (14)2016年12月 (1)2016年11月 (17)2016年10月 (4)2016年9月 (8)2016年8月 (2)2016年6月 (2)博客大牛python_标准风格最新评论@GuoYouLI 这是我从一个前端网站shang 看到的 在后台用 没考虑这么全面...--老板请忘掉哪个bug2. Re:运用JS导出ecxel表格、实现文件重命名@老板请忘掉哪个bug我测试了一下,方法二兼容ie6+ 最后一块优化方法5里面的ie浏览器处理部分,在ie9 10 和最新的没有反应 而在ie7 8正常...--GuoYouLI3. Re:运用JS导出ecxel表格、实现文件重命名@GuoYouLI brtton 按钮的一个a标签的 id 号...--老板请忘掉哪个bug4. Re:运用JS导出ecxel表格、实现文件重命名document.getElementById("dlink") dlink是啥???--GuoYouLI5. Re:datatables插件适用示例@明天OoO你好你长得丑,没办法的!!!!...--老板请忘掉哪个bug阅读排行榜评论排行榜推荐排行榜1. Python全栈---5.1---装饰器(1)2. centos安装Python2.7(1)3. Django之路由、模板和模型系统(1