当前位置: 首页 > 后端技术 > Python

Django笔记21:使用NativeSQL查询数据库

时间:2023-03-25 21:03:44 Python

Django提供了两种执行NativeSQL代码的方式。一种是使用raw()函数,另一种是使用connection.cursor()。但官方建议在使用原生SQL之前,先尝试探索QuerySet提供的各种API。目前官方文档提供的各种API可以满足大部分应用场景。下面是这篇笔记的目录:raw()connection.cursor()多数据库操作1.raw()方法可以用来操作原生SQL,然后返回模型实例:我们以Blog为例,而使用的代码如下:forbloginBlog.objects.raw("select*fromblog_blog"):print(blog)上面代码的效果和Blog.objects.all()得到的一样,但是在某些操作上是不一样的,比如all().count()可以得到总数,但是raw()不能进行这个操作。需要注意的是,raw()不会检测输入的SQL代码。即使我们使用Blog模型查询Entry数据,也可以返回结果,但是返回的结果都是Entry表的属性:forbloginBlog。objects.raw("select*fromblog_entry"):print(blog.__dict__)#__dict__输出blog_entry的所有字段。也就是说,在Blog.objects.raw()中,只有raw()函数是真正起作用的,之前的Blog只是一个架子,或者说是一个途径,引出了raw()函数。在raw()函数的SQL代码中,我们可以自定义选中的字段。如果我们需要使用未选中的字段,系统会再次访问数据库获取。这个操作过程类似于上面描述的defer()。功能相同。对于Blog.objects.raw中的项目(“从blog_blog中选择id”):print(item.__dict__)print(item.id,item.name)print(item.__dict__){'_state':,'id':2}2hunter{'_state':,'id':2,'name':'hunter'}可以看到,返回结果中最开始输出的数据只有id,后面访问name字段时,获取的是name字段的数据。自定义字段必须包含主键。我们在返回自定义字段时,必须包含主键字段,否则获取信息时会报错,比如如下操作:forbloginBlog.objects.raw("selectnamefromblog_blog"):print(blog.__dict__)打印(blog.__dict__)时会报错。数据中没有主键信息。自定义returnnewfield可以和QuerySet的annotate操作一样。获取自定义新字段返回。可以直接根据属性值返回,例如:entry=Entry.objects.raw("select*,date_format(pub_date,'%%Y-%%m')asdate_1fromblog_entry")[0]print(entry.date_1)传递变量给输入的SQL语句传递变量:name="python"Blog.objects.raw("select*fromblog_blogwherename='%s'",[name])2、connection.cursor()Django推出了一种更直接的执行SQL的方式。使用的模块是django.db.connction。使用的游标与pymysql库相同。官方示例如下:fromdjango.dbimportconnectiondefmy_custom_sql(self):withconnection.cursor()ascursor:cursor.execute("UPDATEbarSETfoo=1WHEREbaz=%s",[self.baz])cursor.execute("SELECTfooFROMbarWHEREbaz=%s",[self.baz])row=cursor.fetchone()returnrow需要注意的是,如果有参数传入SQL,有些符号需要可以在数字转义后使用,例如:cursor.execute("SELECTfooFROMbarWHEREbaz='30%'")cursor.execute("SELECTfooFROMbarWHEREbaz='30%%'ANDid=%s",[self.id])其中,第二条语句中的%需要进行转义,以适配获取的数据。fetchone和fetchall()返回的数据只有value值,没有对应的fieldkey。如果适当牺牲性能和内存来换取数据获取的方便和准确,官方提供了这样一种方式:defdictfetchall(cursor):"Returnallrowsfromacursorasadict"columns=[col[0]forcolincursor.description]return[dict(zip(columns,row))forrowincursor.fetchall()]我们执行cursor.execute(sql)后,将cursor作为参数传入dictfetchall函数并返回A字典列表引入了cursor.description,返回一个元组数据,里面的元素也是一个元组。元组的第一个元素是我们选择的字段名所以columns=[col[0]forcolincursor.description]这行代码获取所有指定的字段名称示例:defdictfetchall(cursor):"Returnallrowsfromacursorasadict"columns=[col[0]forcolincursor.description]return[dict(zip(columns,row))forrowincursor.fetchall()]withconnection.cursor()ascursor:sql="selectid,namefromblog_blog"游标.execute(sql)results=dictfetchall(cursor)print(results)#返回结果:#[{'id':20,'name':'name_3'},{'id':21,'name':'name_4'},{'id':1,'name':'name_5'}]在我使用的过程中,我们使用上下文管理器获取游标:withconnection.cursor()ascursor:cursor.execute()所以,之后使用,无需手动关闭。它与以下用法具有相同的效果:c=connection.cursor()try:c.execute(...)finally:c.close()但推荐的方式是上下文管理器,更优雅。3、多数据库操作如果系统使用多个数据库,那么在使用游标时,需要使用django.db.connections模块:fromdjango.dbimportconnectionswithconnections['my_db_alias'].cursor()ascursor:以上就是这篇笔记的全部内容,下篇笔记会简单介绍一下多数据库的操作。