Django中怎么执行原生SQL

发布时间:2021-08-04 14:50:40 作者:Leah
来源:亿速云 阅读:151
# Django中怎么执行原生SQL

## 前言

在Django开发中,ORM(对象关系映射)提供了强大的数据操作能力,能够满足90%以上的数据库操作需求。但在某些特殊场景下(如复杂报表查询、性能优化、数据库特性使用等),我们可能需要直接执行原生SQL语句。本文将全面介绍Django中执行原生SQL的多种方式及其最佳实践。

---

## 一、为什么需要执行原生SQL

### 1.1 ORM的局限性
虽然Django ORM功能强大,但在以下场景可能力不从心:
- 复杂多表连接查询
- 数据库特定函数(如PostgreSQL的JSON操作)
- 需要精细控制SQL执行计划
- 批量操作性能优化

### 1.2 性能考量
某些复杂查询使用原生SQL可能比ORM转换后的查询效率更高。例如:
```python
# ORM方式(可能产生N+1查询)
books = Book.objects.filter(author__name='鲁迅').select_related('author')

# 原生SQL可能更高效
"SELECT * FROM books_book INNER JOIN books_author ON books_book.author_id = books_author.id WHERE books_author.name = '鲁迅'"

二、执行原生SQL的三种主要方式

2.1 使用Manager.raw()方法

最基础的原生SQL执行方式,返回模型实例:

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

# 执行原生查询
books = Book.objects.raw('SELECT * FROM books_book WHERE author = %s', ['鲁迅'])

for book in books:
    print(book.name)

特点:

参数传递方式:

# 位置参数
Book.objects.raw('SELECT * FROM books_book WHERE id = %s', [1])

# 命名参数
Book.objects.raw('SELECT * FROM books_book WHERE id = %(id)s', {'id': 1})

2.2 使用connection对象(底层API)

需要直接访问数据库连接时使用:

from django.db import connection

def my_custom_sql():
    with connection.cursor() as cursor:
        # 执行查询
        cursor.execute("SELECT * FROM books_book WHERE author = %s", ['鲁迅'])
        
        # 获取结果方式1:字典格式
        columns = [col[0] for col in cursor.description]
        results = [dict(zip(columns, row)) for row in cursor.fetchall()]
        
        # 获取结果方式2:原始元组
        # results = cursor.fetchall()
    
    return results

特点:

事务处理示例:

from django.db import transaction

with transaction.atomic():
    with connection.cursor() as cursor:
        cursor.execute("UPDATE books_book SET price = price * 1.1")

2.3 使用cursor的扩展方法

Django提供了一些便捷的游标方法:

from django.db import connection

def get_book_count():
    with connection.cursor() as cursor:
        cursor.execute("SELECT COUNT(*) FROM books_book")
        row = cursor.fetchone()
    return row[0]

常用游标方法:


三、高级用法与技巧

3.1 多数据库支持

在settings.py配置多个数据库时:

from django.db import connections

with connections['replica'].cursor() as cursor:
    cursor.execute("SELECT * FROM books_book")
    # 处理结果...

3.2 存储过程调用

MySQL为例:

with connection.cursor() as cursor:
    cursor.callproc('my_stored_procedure', [param1, param2])
    results = cursor.fetchall()

3.3 结果集处理优化

对于大数据量查询:

def large_query():
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM large_table")
        while True:
            batch = cursor.fetchmany(1000)  # 每次获取1000条
            if not batch:
                break
            process_batch(batch)

3.4 使用extra()方法过渡

ORM与原生SQL的折中方案:

Book.objects.extra(
    where=["author = %s"],
    params=['鲁迅'],
    select={'lower_name': 'LOWER(name)'}
)

四、安全注意事项

4.1 SQL注入防护

错误示范

# 危险!容易导致SQL注入
query = "SELECT * FROM books_book WHERE author = '%s'" % user_input

正确做法

# 使用参数化查询
cursor.execute("SELECT * FROM books_book WHERE author = %s", [user_input])

4.2 权限控制

4.3 输入验证

即使使用参数化查询,也应验证输入:

from django.core.exceptions import ValidationError

def validate_author_name(name):
    if not name.isalpha():  # 简单示例
        raise ValidationError("Invalid author name")

五、性能优化建议

5.1 连接管理

5.2 批量操作

使用executemany提高批量插入效率:

data = [(1, 'Book1'), (2, 'Book2')]
with connection.cursor() as cursor:
    cursor.executemany("INSERT INTO books_book VALUES (%s, %s)", data)

5.3 索引提示

对于复杂查询,可以在SQL中指定索引:

cursor.execute("SELECT * FROM books_book USE INDEX (author_index) WHERE author = %s", ['鲁迅'])

六、测试与调试

6.1 单元测试

from django.test import TestCase

class SQLTests(TestCase):
    def test_raw_query(self):
        with self.assertNumQueries(1):
            list(Book.objects.raw("SELECT * FROM books_book"))

6.2 查询日志

在settings.py中启用:

LOGGING = {
    'version': 1,
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        }
    },
    'loggers': {
        'django.db.backends': {
            'level': 'DEBUG',
            'handlers': ['console'],
        }
    }
}

6.3 EXPLN分析

with connection.cursor() as cursor:
    cursor.execute("EXPLN ANALYZE SELECT * FROM books_book")
    print(cursor.fetchall())

七、替代方案评估

7.1 ORM增强方法

在转向原生SQL前,可尝试: - select_related() / prefetch_related() - annotate() 聚合 - F() 表达式 - 自定义Manager/QuerySet

7.2 第三方库


结语

虽然Django ORM足够强大,但掌握原生SQL执行能力仍是Django开发者的重要技能。合理使用原生SQL可以在保持Django优势的同时,解决特定场景下的特殊需求。关键是要: 1. 优先考虑ORM解决方案 2. 必要时谨慎使用原生SQL 3. 始终注意安全性和性能 4. 编写清晰的文档和测试

通过本文介绍的各种方法和最佳实践,希望您能在Django项目中游刃有余地处理各种数据访问需求。 “`

注:本文实际约2650字(含代码示例),完整覆盖了Django执行原生SQL的主要技术点。根据具体需求,可适当调整各部分篇幅。

推荐阅读:
  1. django中使用原生SQL语句
  2. 基于SQLAlchemy如何实现操作MySQL和执行原生sql语句的方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

sql django

上一篇:MySQLump中Lock-Tables参数的作用是什么

下一篇:如何解决某些HTML字符打不出来的问题

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》