用 ORM 查询和修改数据
学习目标
使用 QuerySet 筛选、排序、读取关联和聚合统计,理解查询何时执行,避免在学习时误改整个表。
为什么用 ORM 仍要理解数据库
ORM 把 Python 表达式转换成 SQL,但数据库仍负责存储、约束和查询执行。Post.objects.filter(...) 返回可继续组合的查询集,通常到遍历、转换列表、取长度等时才执行查询。这称为惰性求值,不是没有查询成本。
get() 用于获取唯一对象,找不到或找到多个都会报错;filter() 可以返回空集合;first() 返回第一条或 None。icontains 表达不区分大小写的包含查找,Q 组合 OR 条件,Count 让数据库计算关联数量。select_related 在同一次查询中获取作者和分类,后面会用测试证明它减少了哪些查询。
具体怎么做
第 10 章数据准备完成后,新增下面的脚本。它不修改数据库,通过 Django shell 执行以获得应用环境:
python manage.py shell -c "exec(open('examples/orm_queries.py').read())"
本章新增或替换的完整文件
以下路径均相对于练习项目根目录。替换同名文件的全部内容;未列出的文件保持上一章版本。
examples/orm_queries.py
from django.db.models import Count, Q
from blog.models import Category, Post
published = Post.objects.filter(status=Post.Status.PUBLISHED)
print("已发布:", published.count())
print(list(published.filter(Q(title__icontains="Django") | Q(body__icontains="模板"))
.values_list("id", "title")[:3]))
print(list(Category.objects.annotate(total=Count("posts")).values("name", "total")))
for post in published.select_related("author", "category")[:3]:
print(post.title, post.author.username, post.category.name)
然后进入 python manage.py shell,独立完成一次可撤销的创建、修改和删除:
from django.contrib.auth import get_user_model
from blog.models import Category, Post
user = get_user_model().objects.get(username="demo-author")
category = Category.objects.get(slug="django")
post = Post.objects.create(title="只用于 ORM 实验的文章", body="这是一篇临时实验文章。", author=user, category=category)
post.title = "已经修改的实验文章"
post.save(update_fields=["title", "updated_at"])
print(Post.objects.get(pk=post.pk).title)
experiment_id = post.pk
post.delete()
print(Post.objects.filter(pk=experiment_id).exists())
这里只删除你刚创建的对象。不要在有重要数据的库中随手执行 Post.objects.all().delete()。带 id 条件的更新可以用 filter(pk=...).update(...),但不会调用模型 save 或信号;第 23 章建立业务服务后,发布操作应走服务方法。
如何确认成功
查询脚本输出 15 篇公开文章、前三个标题以及分类统计。临时文章修改后能读到新标题,删除后 exists 为 False,演示文章仍是 15 篇。
练习:查询标题含“笔记”、作者为 demo-author、状态为 published 的前五篇文章。分别使用 values() 和模型对象读取结果,解释两种返回结构。再用 queryset.query 观察 SQL,但不要把其中的私人数据复制到公开日志。
常见问题
len(queryset) 与 count() 一样吗? 前者可能取出全部对象,后者通常让数据库执行计数,选择取决于你是否马上需要这些对象。先 count 再循环一定更快吗? 不一定,可能增加一次查询。SQL 注入是否完全不必考虑? ORM 的参数化查询能保护正常参数;拼接 raw SQL、order_by 字段或自定义表达式仍需明确白名单和参数化。
官方参考
登录后可保存阅读位置、标记章节完成,并在其他设备继续学习。
登录并保存学习进度 →联系站长
微信:VIPdjango。技术问题建议先在 社区讨论 中交流,方便更多人参与和查阅。
章节讨论 0