Django使用原生SQL查询数据库详解
作者:XHunter 发布时间:2024-01-21 00:58:08
Django 提供了两种方式来执行原生 SQL 代码。
一种是使用 raw() 函数,一种是 使用 connection.cursor()。
但是官方还是推荐在使用原生 SQL 之前,尽量的先去探索一下 QuerySet 提供的各种 API。
目前而言,官方文档提供的各种 API 是能够满足绝大多数的应用场景的。
以下是本篇笔记的目录:
raw()
connection.cursor()
多数据库操作
1、raw()
这个方法可以用来操作原生 SQL,然后返回 model 实例:
我们以 Blog 作为示例,使用的代码如下:
for blog in Blog.objects.raw("select * from blog_blog"):
print(blog)
上面这段代码的作用效果跟 Blog.objects.all() 获取的结果是一样的,但是在一些操作上又不太一样,比如 all().count() 可以获取到 总数,但是 raw() 就不能进行这种操作。
需要注意的是,raw() 并不会去检测输入的 SQL 代码,即使我们使用 Blog 这个model 来查询 Entry 的数据,也能够返回结果,不过返回的都是 Entry 表的属性:
for blog in Blog.objects.raw("select * from blog_entry"):
print(blog.__dict__) # __dict__ 输出的都是 blog_entry 的字段
也就是说 在 Blog.objects.raw() 中,真正起作用的只有 raw() 这一个函数,前面的 Blog 不过是一个架子,或者途径,是为了引出 raw() 这个函数。
自定义返回字段
在 raw() 函数的 SQL 代码里,我们可以自定义选中的字段,如果需要使用没有选中的字段,那么系统会再次访问数据库来获取,这个操作过程就跟前面介绍的 defer() 函数一样。
for item in Blog.objects.raw("select id from blog_blog"):
print(item.__dict__)
print(item.id, item.name)
print(item.__dict__)
{'_state': <django.db.models.base.ModelState object at 0x7fbd4165b6a0>, 'id': 2}
2 hunter
{'_state': <django.db.models.base.ModelState object at 0x7fbd4165b6a0>, 'id': 2, 'name': 'hunter'}
可以看到,返回的结果中先输出的数据只有 id,后面,当我们访问 name 字段的时候,又去获取了 name 字段的数据。
自定义字段必须包含主键
当我们自定义字段返回的时候,必须是要包含主键字段的,否则在我们获取信息的时候会报错,比如下面的操作:
for blog in Blog.objects.raw("select name from blog_blog"):
print(blog.__dict__)
在 print(blog.dict) 的时候就会报错,数据中没有主键信息
自定义返回新字段
可以跟 QuerySet 的 annotate 操作一样,自定义新字段返回,获取的时候可以直接根据属性值返回,比如:
entry = Entry.objects.raw("select *, date_format(pub_date, '%%Y-%%m') as date_1 from blog_entry")[0]
print(entry.date_1)
传递变量
给输入的 SQL 语句传递变量:
name = "python"
Blog.objects.raw("select * from blog_blog where name = '%s'", [name])
2、connection.cursor()
Django 推出了一种更加直接执行 SQL 的方式,用到的模块是 django.db.connction,用到的 cursor 和 pymysql 的库是一样的用法,官方给出的示例如下:
from django.db import connection
def my_custom_sql(self):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
需要注意,如果有参数传入 SQL 中,需要对一些符号进行转义之后才可以使用,比如:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
其中,第二条语句的 % 需要进行转义
适配fetch的数据
通过 fetchone 和 fetchall() 返回的数据是只有 value 值的,没有对应的字段 key,如果可以适当的牺牲性能和内存,来换取获取数据的便利和准确性,官方提供了这样一种方式:
def dictfetchall(cursor):
"Return all rows from a cursor as a dict"
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
在我们执行完 cursor.execute(sql) 之后,把 cursor 作为参数传入 dictfetchall 函数中,就可以返回一个字典列表
介绍一下 cursor.description,这个返回的是一个元组数据,里面的元素也是一个元组,元组的第一个元素就是我们 select 的字段名。
所以 columns = [col[0] for col in cursor.description] 这一行代码获取的是指定的所有字段名
使用示例:
def dictfetchall(cursor):
"Return all rows from a cursor as a dict"
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
with connection.cursor() as cursor:
sql = "select id, name from blog_blog"
cursor.execute(sql)
results = dictfetchall(cursor)
print(results)
# 返回的结果:
# [{'id': 20, 'name': 'name_3'}, {'id': 21, 'name': 'name_4'}, {'id': 1, 'name': 'name_5'}]
在我使用的过程中,我们使用的是上下文管理器的方式来获取游标:
with connection.cursor() as cursor:
cursor.execute()
所以,使用完之后,不需要手动关闭,他与下面的用法效果是一致的:
c = connection.cursor()
try:
c.execute(...)
finally:
c.close()
但推荐的还是上下文管理器的方式,更优雅。
3、多数据库操作
如果系统用到了多个数据库,那么在使用 cursor 的时候,就需要使用到 django.db.connections 模块:
from django.db import connections
with connections['my_db_alias'].cursor() as cursor:
pass
本文详细介绍了在Django中使用原生SQL进行数据库查询的方法,包括优点和使用方式。通过使用原生SQL,我们可以更灵活、更高效地进行数据库操作,特别是在涉及到复杂的查询或性能要求较高的情况下。但需要注意的是,使用原生SQL查询可能会带来安全问题,需要谨慎使用,并且要遵守Django框架的开发规范。
来源:https://www.cnblogs.com/hunterxiong/p/17304181.html


猜你喜欢
- 前言:如何做到,控制多设备并行执行测试用例呢。思路篇我们去想下,我们可以获取参数的信息,和设备的信息,那么我们也可以针对每台设备开启不一样的
- git还原到某次commit并强制推送远程不可逆提交一、reset1.git log查看提交记录git log2.选择某次提交的commit
- 老婆大人让俺帮她通过Excel生成百人的准考证,她们学校打算来一次高考模拟。由于高考改革,每个学生的考试科目不一样,需要自动生成一下。我一个
- 最近在做的一个项目中需要使用到HTML5中引入的WebSocket技术,本来以为应该很容易就能搞定,谁知道在真正上手开发了以后才发现有很多麻
- 一、Python pass语句在实际开发中,有时候我们会先搭建起程序的整体逻辑结构,但是暂时不去实现某些细节,而是在这些地方加一些注释,方面
- 1.安装mysql5.6 docker run mysql:5.6等所有项目都是Download complet 安装完毕5.6: Pull
- 对,你没看错,这是我初学 python 时的灵魂发问。我们总会在class里面看见self,但是感觉他好像也没什么用处,就是放在那里占个位子
- 1.安装python windows版本好:python-2.5.1.msi2.安装pycrypto windows版本号:py
- 1.分包背景这里首先介绍下MultiDex的产生背景。当Android系统安装一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的
- 语法plt.scatter(x, y, s=20, c='b')大小s默认为20,s=0时点不显示;颜色c默认为蓝色。为每一
- 这篇文章主要介绍了Python实现序列化及csv文件读取,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 先了解如何利用python语言实现以平面和标记物进行姿态估计本实验只是先实现一个简单的小例子。简单来说就是先识别出图像中的参考面,再拍摄一张
- 在处理批量更新某些数据的时候,如果跟你更新的字段的值都一样,比如某个状态都更新为某个固定值,直接用update table set xxx=
- 今天一个同事报告一个问题,表都不能使用了,检查了一下,发现问题 db2 => select * from testACTNO ACTK
- 今天小编给大家详细的讲解一下Scrapy爬虫框架,希望对大家的学习有帮助。1、Scrapy爬虫框架Scrapy是一个使用Python编程语言
- 1、模拟退火算法退火是金属从熔融状态缓慢冷却、最终达到能量最低的平衡态的过程。模拟退火算法基于优化问题求解过程与金属退火过程的相似性,以优化
- Python decimal 模块Python中的浮点数默认精度是15位。Decimal对象可以表示任意精度的浮点数。getcontext函
- 9月23,Django 发布了2.0a1版本,这是一个 feature freeze 版本,如果没有什么意外的话,2.0正式版不会再增加新的
- 第一种, 使用create_connection链接,需要pip install websocket-client (此方法不建议使用,链接
- 首先我们从一个小程序导入,各定一个list,找出其中的素数,我们会这样写import mathdef is_Prims(number): &