深度分析Django基础教程之初识QuerySet
朋友们,兄弟们,代码摸鱼者们!今天咱们不聊Django怎么安装,也不扯什么MTV模式。那些都是基本功,是扎马步。今天,我要带你解锁Django世界里一个真正让你“起飞”的技能——QuerySet。
你可以把它想象成Django送给你的一个万能遥控器。你想对数据库里的数据做点啥,比如“把所有名字里带‘帅’的用户找出来”、“把点赞数超过100的文章按时间倒序排好”、“把商品库存自动减一”……你不用苦哈哈地去写那些又臭又长的SQL语句,只需要用Python的风格,像搭乐高一样,轻松写几行代码,QuerySet就会在背后帮你把一切搞定。
更神奇的是,这根“魔法棒”还自带**“懒癌”属性和“链式”绝技**,用好了你就能大喊一声:“键来!”(不对,是“数据来!”)。
一、QuerySet到底是啥?它不是列表,是“承诺”!
刚入门的小伙伴最容易产生的误解就是:Article.objects.all() 这不就是返回了一个包含所有文章的列表吗?
错!大错特错!
你看到的 Article.objects.all(),它返回的不是数据本身,而是一个 “数据的承诺”,或者说是一张 “还没兑奖的彩票”。
# 想象你在奶茶店点单
queryset = Article.objects.all() # 你只是对店员说:“把你们店里所有奶茶都给我准备好。”
# 这时候,店员(Django)只是记下了你的要求,但根本没开始动手做奶茶。
# 所以,数据库此时还没有任何查询操作!这,就是“懒惰”的奥秘。
# 什么时候店员才开始真正做奶茶呢?
for article in queryset: # 当你说:“好了,现在一杯一杯拿给我。”
print(article.title)
# 或者
list(queryset) # 当你说:“别一杯一杯了,全部打包到一个袋子里给我。”
# 甚至
if queryset: # 当你需要检查“你到底有没有奶茶”时。
print("Yes, we have articles!")
这个“懒惰”的特性,是QuerySet最核心的魔法。它意味着你可以不断地、链式地修饰你的查询,而Django会积攒你的所有指令,直到非用不可的那一刻,才会一次性访问数据库,生成最有效的SQL语句。
这避免了大量不必要的数据库查询,性能蹭蹭往上涨!
二、QuerySet核心“咒语”大全:从入门到上头
好了,我们知道QuerySet很“懒”了。那怎么驱使这个“懒家伙”为我们干活呢?当然是靠各种强大又优雅的“咒语”(方法)啦!
1. 基础筛选三剑客:all, filter, get, exclude
all(): “我全都要!”——获取某个模型下的所有对象。filter(**kwargs): “我要符合条件的!”——这是你最常用的,相当于SQL的WHERE。
# 找出所有标题中包含“Django”的文章
django_articles = Article.objects.filter(title__contains='Django')
# 找出所有发布时间在2023年之后且点赞数大于50的文章
popular_recent_articles = Article.objects.filter(
publish_date__year__gte=2023,
likes__gt=50
)
# 看,像不像在说人话?
get(**kwargs): “我只要那一个,独一无二的!”——返回唯一匹配的对象。如果没找到会抛DoesNotExist异常,找到多个会抛MultipleObjectsReturned异常。用于你确信存在且唯一的情况,比如根据主键查。
# 根据主键ID获取一篇文章
try:
that_article = Article.objects.get(id=1)
except Article.DoesNotExist:
print("文章不存在哦!")
exclude(**kwargs): “除了这些,我都要!”——排除符合条件的数据。
# 找出所有不是草稿状态的文章
published_articles = Article.objects.exclude(status='draft')
2. “链式施法”:把复杂查询写成一首诗
这是QuerySet最优雅的地方!它的多数方法都会返回一个新的QuerySet,所以你可以像链条一样一个接一个地调用。
# 一个复杂的查询:找出2023年发布的,标题不含“测试”,并且点赞数大于10的文章,按发布时间倒序排列。
result = Article.objects.all() \
.filter(publish_date__year=2023) \
.exclude(title__contains='测试') \
.filter(likes__gt=10) \
.order_by('-publish_date')
# 看,这一长串逻辑,读起来是不是非常顺畅?
# Django最终会把这一串链式调用,组合成一条高效的SQL语句执行。
3. 进阶魔法:F对象 与 Q对象
当你觉得上面的操作只是小打小闹时,真正的魔法来了。
F对象:自己跟自己比!
通常,你只能拿模型的字段和一个常量比较。但如果你想拿一个字段和另一个字段比较呢?
from django.db.models import F
# 找出所有“评论数”大于“点赞数”的文章(这是什么奇葩文章?)
weird_articles = Article.objects.filter(comment_count__gt=F('likes'))
# 更实用的:将所有文章的点赞数+1
Article.objects.update(likes=F('likes') + 1)
# 一条SQL搞定,不需要先取出数据,再用Python计算,最后保存。避免了并发问题!
Q对象:构建复杂的逻辑关系(与或非)
filter里的多个条件是AND关系。如果你想用OR或者NOT怎么办?Q对象闪亮登场!
from django.db.models import Q
# 找出标题包含“Django” OR 标题包含“Python”的文章
articles = Article.objects.filter(
Q(title__contains='Django') | Q(title__contains='Python')
)
# 找出作者是“小明” AND (状态是“已发布” OR 点赞数>100)的文章
complex_articles = Article.objects.filter(
Q(author='小明') & (Q(status='published') | Q(likes__gt=100))
)
# 这不就是SQL里的括号吗?用Q对象,再复杂的逻辑也能清晰表达。
三、完整示例:打造一个迷你博客文章管理器
光说不练假把式,让我们来亲手搭建一个场景,看看QuerySet在实战中多么威武。
1. 模型定义 (models.py)
from django.db import models
class Article(models.Model):
STATUS_CHOICES = (
('draft', '草稿'),
('published', '已发布'),
('archived', '归档'),
)
title = models.CharField(max_length=200, verbose_name='标题')
content = models.TextField(verbose_name='内容')
author = models.CharField(max_length=100, default='匿名侠', verbose_name='作者')
publish_date = models.DateTimeField(auto_now_add=True, verbose_name='发布时间') # 创建时自动设置
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft', verbose_name='状态')
likes = models.PositiveIntegerField(default=0, verbose_name='点赞数')
comment_count = models.PositiveIntegerField(default=0, verbose_name='评论数')
def __str__(self):
return self.title
class Meta:
db_table = 'article' # 自定义表名
ordering = ['-publish_date'] # 默认按发布时间倒序排列
(记得 python manage.py makemigrations 和 python manage.py migrate 哦!)
2. 在Django Shell中施展魔法
打开 python manage.py shell,我们来玩转数据。
# 导入模型
from myapp.models import Article
from django.db.models import F, Q
from datetime import datetime
# 1. 创建一些测试数据(这里简单创建,实际项目会用Fixtures或管理后台)
Article.objects.create(title="我的第一篇Django文章", content="...", status='published', likes=10)
Article.objects.create(title="Python入门指南", content="...", status='published', likes=25, comment_count=30)
Article.objects.create(title="一个未完成的草稿", content="...", status='draft')
Article.objects.create(title="Django QuerySet深度研究", content="...", status='published', likes=50, comment_count=45)
Article.objects.create(title="归档的老新闻", content="...", status='archived', likes=5)
# 2. 基础查询:获取所有已发布文章
published = Article.objects.filter(status='published')
for article in published:
print(f"- {article.title} (点赞: {article.likes})")
# 3. 链式查询:获取最近发布的、点赞超过20的已发布文章,并按点赞数排序
popular = Article.objects.filter(status='published').filter(likes__gte=20).order_by('-likes')
print("\n--- 热门文章 ---")
for article in popular:
print(f"- {article.title} (点赞: {article.likes})")
# 4. 使用Q对象:搜索标题含有“Django”或“Python”的已发布文章
search_results = Article.objects.filter(
status='published'
).filter(
Q(title__contains='Django') | Q(title__contains='Python')
)
print("\n--- 搜索结果 ---")
for article in search_results:
print(f"- {article.title}")
# 5. 使用F对象:找出评论比点赞还多的“争议”文章
controversial = Article.objects.filter(comment_count__gt=F('likes'))
print("\n--- 争议文章(评论>点赞)---")
for article in controversial:
print(f"- {article.title} (赞: {article.likes}, 评: {article.comment_count})")
# 6. 性能相关:使用`only`只获取需要的字段,避免查询所有数据(适用于大文本字段)
# 假设content字段很大,我们只需要标题和作者
lightweight_articles = Article.objects.filter(status='published').only('title', 'author')
for article in lightweight_articles:
print(f"- {article.title} by {article.author}") # 此时访问content才会触发额外查询
四、总结:让你的Django技能“起飞”
看到这里,你是不是对QuerySet刮目相看了?它绝不仅仅是一个简单的数据获取工具,而是一个功能丰富、设计精巧的查询构建器。
记住它的三大特性:
- 懒惰(Lazy):不评估不查询,性能优化的基石。
- 链式(Chainable):让复杂查询的构建过程清晰、易读、易维护。
- 强大(Powerful):
F对象和Q对象让你拥有应对任何复杂业务场景的能力。
下次当你写Django代码时,请有意识地多用用QuerySet的这些高级特性。别再手动循环过滤,别再写重复的代码。用好这根“魔法棒”,你会发现,和数据库打交道,原来可以如此轻松、优雅和高效。

被折叠的 条评论
为什么被折叠?



