在 Django 开发中,通常依赖于 Django ORM 来处理数据库操作,它提供了一个高级的抽象层,使得数据库交互更加直观和安全。然而,有时可能需执行一些复杂的查询或数据库操作,这些操作可能超出了 ORM 的能力范围(比如动态生成DB表结构等等)。在这些情况下,可以使用 Django 的底层数据库 API 来执行原生 SQL 语句。本文将介绍如何在 Django 中使用 connection.cursor()
来执行 SQL 语句。
要执行SQL查询,首先需要获取数据库的游标:
from django.db import connection
cursor = connection.cursor()
有了游标,才可执行SQL查询:
cursor.execute("SELECT * FROM my_table")
执行查询后,你可以使用 fetchone()
、fetchmany()
或 fetchall()
方法来检索结果:
rows = cursor.fetchall() # 读取所有数据,下面有介绍其他方法
for row in rows:
print(row)
print(row[0])
完成数据库操作后,应关闭游标以释放资源:
cursor.close()
为简化资源管理,推荐使用 with
语句作为上下文管理器,会在代码块执行完毕后自动关闭游标:
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM my_table")
有了游标对象,可用它来执行 SQL 查询。以下是一个执行 SELECT 查询的例子:
cursor.execute("SELECT * FROM myapp_mymodel")
你也可以使用参数化查询来防止 SQL 注入攻击:
cursor.execute("SELECT * FROM myapp_mymodel WHERE id = %s", [some_id])
执行查询后,你可以使用游标的 fetchone()
、fetchmany()
或 fetchall()
方法来检索结果。
# 获取单个结果
row = cursor.fetchone()
# 获取多个结果(可以指定数量)
rows = cursor.fetchmany(size=5)
# 获取所有结果
rows = cursor.fetchall()
一些读取操作:
# 读取
for tup in cursor.fetchall():
data.append({"id": tup[0], "name": tup[1], "teach_id": tup[2], "teacher_name": tup[3], "content": tup[4]})
# 判断
one = cursor.fetchone()
if one is not None:
return Response({'message': f'{one[0]}已存在'}, status=status.HTTP_400_BAD_REQUEST)
但有发现,返回的都是一个列表,而无法直接用数据库表字段名锁定,这是有个其他方法,能锁定到字段名对应的索引列。
columns = {col[0]: idx for idx, col in enumerate(cursor.description)}
print(columns)
# {'create_time': 0, 'update_time': 1, 'uid': 2, 'task_uid': 3, 'usage': 4, 'type': 5}
# k-v 为 字段名-索引
# 获取所有行
rows = cursor.fetchall()
for row in rows:
# 使用列名来访问数据
name = row[columns['name']]
print(name)
在执行更新、删除或插入操作时,你可能需要使用事务来确保数据的一致性。Django 默认在每个请求的末尾自动提交事务,但你可以手动控制事务的提交和回滚(在执行 INSERT、UPDATE 或 DELETE 操作时,大可能控制事务的提交和回滚):
try:
with connection.cursor() as cursor:
# 执行SQL查询
cursor.execute("UPDATE my_table SET field = %s WHERE another_field = %s", ['new_value', 'some_value'])
# 提交更改
connection.commit()
except Exception as err:
# 发生异常,回滚事务
connection.rollback()
print("An error occurred: ", err)
注:
connection.cursor()
时,记得遵循数据库的最佳实践,比如使用参数化查询来防止 SQL 注入。