Django 模型与 ORM 全解析(二):数据库操作
引言:为什么要学 Django ORM?
- 什么是 Django ORM?—— 简化数据库操作的 “桥梁”,它允许开发者使用 Python 语法而非原生 SQL 与数据库交互,无需关注底层数据库类型的差异。
- 学习目标:掌握 ORM 核心增删改查(CRUD)及进阶用法,高效完成数据库操作。
- 博客结构:从基础到高级,从核心操作到扩展技巧,逐步拆解 ORM 实用方法。
一、查询操作(Read)
查询是 ORM 最常用的操作,Django 提供基础查询和高级查询两类方法,满足不同场景的需求。
1.1 基本查询
基本查询用于获取单个或多个符合条件的记录,核心方法如下:
方法 | 描述 | 适用场景 |
---|---|---|
all() | 获取所有记录(返回QuerySet) | 列表页展示所有数据 |
get (条件) | 获取单个符合条件的记录(返回实例) | 根据主键 / 唯一字段获取单条数据 |
filter (条件) | 获取多个符合条件的记录(返回 QuerySet) | 按条件筛选数据(如状态 = 激活) |
exclude (条件) | 排除符合条件的记录(返回 QuerySet) | 筛选 “不满足条件” 的数据 |
代码示例:
# 获取所有对象
Model.objects.all() # 根据条件获取单个对象
Model.objects.get(id=1) # 根据条件过滤对象
Model.objects.filter(name='example') # 排除特定条件的对象
Model.objects.exclude(status='inactive')
注意:
get(条件)
若未找到记录或找到多条记录,会抛出异常(DoesNotExist
或MultipleObjectsReturned
),需谨慎使用。- 若需 “安全获取单个记录”,可结合
get_or_create()
(后续创建操作介绍)。
1.2 高级查询
高级查询是在基本查询的基础上,实现排序、限制结果、判断存在性等功能:
链式查询:将多个查询方法串联,实现多条件筛选(QuerySet 支持链式调用):
# 筛选“作者为张三”且“状态为激活”的图书 books = Book.objects.filter(author='张三').exclude(is_active=False)
排序:通过
order_by(字段)
实现排序,字段前加 “-” 表示降序:# 按出版日期升序(旧→新) books = Book.objects.order_by('publication_date') # 按出版日期降序(新→旧) books = Book.objects.order_by('-publication_date')
限制结果数量:通过切片
[:n]
获取前 n 条记录(类似 Python 列表切片,不支持负索引):# 获取前5条图书记录(用于分页或列表页默认展示) books = Book.objects.all()[:5]
判断是否存在:通过
exists()
判断是否有符合条件的记录,返回布尔值(比len()
更高效,无需加载所有数据):# 判断是否存在“作者为张三”的图书 has_zhang_books = Book.objects.filter(author='张三').exists() print(has_zhang_books) # 存在返回True,不存在返回False
数量统计:通过
count()
统计符合条件的记录数,返回整数(比len(QuerySet)
更高效,直接执行COUNT(*)
SQL):# 统计所有图书数量 total_books = Book.objects.count() # 统计“激活状态”的图书数量 active_books_count = Book.objects.filter(is_active=True).count()
二、 创建操作(Create)
创建操作用于向数据库插入新记录,Django 提供单个创建和批量创建两种方式。
2.1 单个对象创建
单个对象创建有三种常用方法,适用于不同场景:
方法 1:先实例化再保存(分步操作,可在保存前修改字段):
# 1. 实例化模型(未保存到数据库) book = Book(title='Django 5教程', author='张三', price=99.99) # 2. (可选)修改字段 book.publication_date = '2024-05-01' # 3. 保存到数据库(执行INSERT SQL) book.save()
方法 2:直接创建(一步完成,通过
objects.create()
):# 直接创建并保存到数据库,返回创建的实例 book = Book.objects.create(title='Django 5教程',author='张三',price=99.99,publication_date='2024-05-01' )
方法 3:获取或创建(避免重复创建,通过
objects.get_or_create()
):若记录已存在,则获取;若不存在,则创建,返回(实例, 是否创建成功)
的元组:# 根据title查询,若不存在则创建(defaults为创建时的其他字段) book, created = Book.objects.get_or_create(title='Django 5教程', # 查询条件(需是唯一字段或组合唯一)defaults={'author': '张三','price': 99.99,'publication_date': '2024-05-01'} ) print(created) # 第一次创建返回True,后续获取返回False
2.2 批量创建:bulk_create ()
若需创建多条记录,使用bulk_create()
比循环调用create()
更高效(仅执行一次 SQL,减少数据库连接次数):
# 1. 构建多个模型实例(未保存)
book_list = [Book(title='Django 5教程1', author='张三', price=99.99),Book(title='Django 5教程2', author='张三', price=89.99),Book(title='Django 5教程3', author='张三', price=79.99),
]
# 2. 批量保存到数据库
Book.objects.bulk_create(book_list)
三、更新操作(Update)
更新操作用于修改数据库中已有的记录,支持单个更新和批量更新。
3.1 单个对象更新
单个对象更新有三种方法,根据是否需要 “先获取实例” 选择:
方法 1:获取对象后修改字段→保存(适用于需先处理数据的场景):
# 1. 获取实例
book = Book.objects.get(id=1)
# 2. 修改字段
book.title = 'Django 5教程(第二版)'
book.price = 109.99
# 3. 保存到数据库(执行UPDATE SQL)
book.save()
方法 2:filter ()+update ()(直接更新,无需先获取实例,更高效):适用于明确条件、无需修改实例其他属性的场景,直接执行UPDATE
SQL:
# 根据id更新title和price(无需获取实例)
Book.objects.filter(id=1).update(title='Django 5教程(第二版)',price=109.99
)
方法 3:更新或创建(update_or_create()
,不存在则创建):类似get_or_create()
,但会更新已存在的记录,返回(实例, 是否创建成功)
:
# 根据id更新,若id=1不存在则创建
book, created = Book.objects.update_or_create(id=1, # 查询条件(通常是主键)defaults={'title': 'Django 5教程(第二版)','price': 109.99}
)
print(created) # 存在则更新,返回False;不存在则创建,返回True
3.2 批量更新:filter ().update ()
批量更新是对符合条件的所有记录执行统一更新,适用于 “批量修改状态” 等场景:
# 1. 批量将“待处理”状态的订单改为“已处理”
Order.objects.filter(status='pending').update(status='processed')# 2. 批量更新所有记录的“更新时间”为当前时间(需导入timezone)
from django.utils import timezone
Book.objects.all().update(updated_at=timezone.now())
四、删除操作(Delete)
删除操作用于从数据库中移除记录,支持单个删除和批量删除,操作需谨慎(删除后无法恢复)。
4.1 单个对象
删除单个对象删除有两种方法,与更新操作逻辑类似:
方法 1:获取对象后删除(适用于需先验证的场景):
# 1. 获取实例
book = Book.objects.get(id=1)
# 2. 删除记录(执行DELETE SQL)
book.delete()
方法 2:filter ().delete ()(直接删除,无需先获取实例):
# 根据id直接删除记录
Book.objects.filter(id=1).delete()
4.2 批量删除
批量删除是对符合条件的所有记录执行删除,需特别注意all().delete()
(删除所有记录):
# 1. 批量删除“未激活”的图书
Book.objects.filter(is_active=False).delete()# 2. 删除所有图书(谨慎使用!会清空整个表)
Book.objects.all().delete()
五、其他常用 ORM 操作
除了 CRUD,Django ORM 还提供聚合查询、关联查询优化、原生 SQL 查询等高级功能,满足复杂业务需求。
5.1 聚合查询:Count、Sum、Avg、Max、Min
聚合查询用于对字段进行统计计算(如求和、平均值),需从django.db.models
导入聚合函数,示例如下:
from django.db.models import Count, Sum, Avg, Max, Min# 1. 统计图书总数(等价于Book.objects.count())
total_books = Book.objects.aggregate(total=Count('id'))
print(total_books) # 输出{'total': 100}(假设共100本)# 2. 计算所有图书的价格总和
total_price = Book.objects.aggregate(total=Sum('price'))
print(total_price) # 输出{'total': Decimal('9999.99')}# 3. 计算图书的平均价格
avg_price = Book.objects.aggregate(avg=Avg('price'))
print(avg_price) # 输出{'avg': Decimal('99.99')}# 4. 获取图书的最高价格和最低价格
price_extremes = Book.objects.aggregate(max=Max('price'),min=Min('price')
)
print(price_extremes) # 输出{'max': Decimal('199.99'), 'min': Decimal('29.99')}
5.2 关联查询优化:select_related 与 prefetch_related
关联查询若不优化,会产生 “N+1 查询问题”(如查询 10 本书,先查 10 本图书,再查每本的作者,共执行 11 次 SQL)。Django 提供两种优化方法:
1.select_related:用于 “一对一” 或 “多对一”(ForeignKey)关联,一次性加载关联对象(左连接),适用于 “正向关联查询”:
# 优化前:查询10本书,会执行1(查图书)+10(查每本的作者)=11次SQL
books = Book.objects.all()[:10]
for book in books:print(book.author.name) # 每次循环都执行一次查作者的SQL# 优化后:仅执行1次SQL,一次性加载图书和关联的作者
books = Book.objects.select_related('author').all()[:10]
for book in books:print(book.author.name) # 无需额外执行SQL
2.prefetch_related:用于 “多对多” 或 “一对多反向查询”,先查询主对象,再批量查询关联对象(两次 SQL),适用于 “反向关联或多对多查询”:
# 优化前:查询10个作者,执行1(查作者)+10(查每个作者的图书)=11次SQL
authors = Author.objects.all()[:10]
for author in authors:print(author.book_set.all()) # 每次循环执行一次查图书的SQL# 优化后:仅执行2次SQL(先查作者,再批量查所有作者的图书)
authors = Author.objects.prefetch_related('book_set').all()[:10]
for author in authors:print(author.book_set.all()) # 无需额外执行SQL
5.5.3 原生 SQL 查询:raw () 与 extra ()
若 ORM 查询无法满足复杂需求(如多表联查、自定义函数),可通过原生 SQL 查询直接执行 SQL 语句:
1.raw():执行原生 SELECT 语句,返回RawQuerySet
(可迭代的模型实例):
# 执行原生SQL,查询id=1的图书(参数用%s占位,避免SQL注入)
books = Book.objects.raw('SELECT * FROM book WHERE id = %s', [1])
# 迭代获取结果(RawQuerySet是惰性的,仅在迭代时执行SQL)
for book in books:print(book.title)
2.extra():在 ORM 查询的基础上,添加额外的 SQL 条件(如 WHERE、SELECT),适用于简单的原生 SQL 补充:
# 添加额外的WHERE条件:查询标题包含“Django”的图书
books = Book.objects.extra(where=["title LIKE %s"], # SQL条件(占位符%s)params=['%Django%'] # 参数(自动转义,避免注入)
)# 添加额外的SELECT字段:查询图书并计算“折扣价”(假设discount=20)
books = Book.objects.extra(select={'discount_price': 'price * 0.8'} # 自定义SELECT字段
)
for book in books:print(book.discount_price) # 访问自定义字段
附录:Django ORM 常用方法速查表
CRUD 核心方法对比表
方法分类 | 方法名 | 语法示例 | 用途 | 适用场景 |
---|---|---|---|---|
查询 | all() | Model.objects.all() | 获取所有数据 | 数据列表展示 |
查询 | get() | Model.objects.get(id=1) | 获取单个唯一数据 | 按主键查询详情 |
创建 | create() | Model.objects.create(name='test') | 单个对象创建 | 新增单条数据 |
创建 | bulk_create() | Model.objects.bulk_create([obj1, obj2]) | 批量对象创建 | 批量导入数据 |
更新 | update() | Model.objects.filter(id=1).update(name='new') | 批量 / 单个字段更新 | 无需触发 save() 时 |
删除 | delete() | Model.objects.filter(id=1).delete() | 批量 / 单个对象删除 | 移除指定条件数据 |
聚合 / 关联查询关键 API 汇总
查询类型 | 核心 API | 适用关系 | 作用 |
---|---|---|---|
聚合查询 | Count /Sum | 单表数据统计 | 计算记录数、总和等 |
关联查询 | select_related() | 一对一、一对多 | 连表预加载关联数据 |
关联查询 | prefetch_related() | 多对多、反向一对多 | 预加载关联数据,避免 N+1 查询 |