mssql-django 中的原始 SQL 查询

本文介绍如何针对 Django 应用程序中的SQL Server执行原始 SQL 查询。 原始 SQL 对于未通过 Django ORM 公开的操作非常有用,例如复杂的 Transact-SQL(T-SQL)、空间查询或性能关键操作。

使用 connection.cursor()

直接通过 Django connection 的对象访问数据库游标:

from django.db import connection

def get_server_version():
    with connection.cursor() as cursor:
        cursor.execute("SELECT @@VERSION;")
        row = cursor.fetchone()
    return row[0]

with 语句确保游标在使用后正确关闭。

参数化查询

始终使用参数化查询来防止 SQL 注入。 将参数作为列表传递:

Note

以下示例使用 Django 的默认表命名约定<app_label>_<model_name>(例如)。 myapp_product 如果在模型中db_table重写Meta,请替换该名称。 还可以通过 Product._meta.db_table 在运行时读取解析后的名称。

from django.db import connection

def get_products_by_price(min_price, max_price):
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT id, name, price FROM myapp_product WHERE price BETWEEN %s AND %s;",
            [min_price, max_price],
        )
        results = cursor.fetchall()
    return results

Important

切勿使用字符串格式或 f 字符串在 SQL 查询中嵌入值。 始终使用参数化查询(%s 带有参数列表的占位符),以防止 SQL 注入。

获取结果

Django 的游标提供了几种用于检索结果的方法:

from django.db import connection

def demonstrate_fetch_methods():
    with connection.cursor() as cursor:
        cursor.execute("SELECT id, name FROM myapp_product;")

        # Fetch one row
        row = cursor.fetchone()

        # Fetch the next 10 rows (continues from where fetchone stopped)
        rows = cursor.fetchmany(10)

        # Fetch all remaining rows (continues from where fetchmany stopped)
        all_rows = cursor.fetchall()

以字典的形式返回结果

使用 cursor.description 将行转换为字典:

from django.db import connection

def dictfetchall(cursor):
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]

def get_all_products():
    with connection.cursor() as cursor:
        cursor.execute("SELECT id, name, price FROM myapp_product;")
        return dictfetchall(cursor)

将 Manager.raw() 用于模型查询

如果想要原始 SQL 但仍需要 Django 模型实例,请使用 Manager.raw()

from myapp.models import Product

products = Product.objects.raw(
    "SELECT id, name, price FROM myapp_product WHERE price > %s",
    [10.00],
)

for product in products:
    print(f"{product.name}: ${product.price}")

查询必须返回模型主键中定义的所有字段。 其他字段加载延迟。

执行 DDL 语句

对于 Django 不直接支持的架构操作,请使用原始 SQL:

from django.db import connection

def create_index():
    with connection.cursor() as cursor:
        cursor.execute(
            "CREATE INDEX IX_product_name ON myapp_product (name) "
            "INCLUDE (price);"
        )

多个数据库连接

如果使用多个数据库,请指定要使用的连接:

from django.db import connections

def query_reporting_db():
    with connections["reporting"].cursor() as cursor:
        cursor.execute("SELECT COUNT(*) FROM myapp_product;")
        return cursor.fetchone()[0]