Django ORM查询技巧全解析
一、单表查询:
基础查询:
查询所有数据(all ())
- 用法:
User.objects.all()
- 说明:返回表中所有记录,结果为 QuerySet 集合
- 模板中使用:
{% for user in userlist %}{{ user.username }}{% endfor %}
- 用法:
精确查询单个对象(get ())
- 用法:
User.objects.get(username="乔巴43324")
- 说明:返回满足条件的唯一对象
- 注意:若查询结果不存在或多于一个,会抛出异常
- 模板中使用:直接访问属性
{{ user.username }}
- 用法:
条件筛选查询(filter ())
- 用法:
User.objects.filter(username="路飞123")
- 说明:返回满足条件的所有记录,结果为 QuerySet 集合
- 特点:无匹配结果时返回空集合,不会报错
- 模板中使用:
{% for user in userlist %}{{ user.username }}{% endfor %}
- 用法:
查询指定字段(values ())
- 用法:
User.objects.all().values("id","username")
- 说明:返回包含指定字段的字典集合,格式为
{key:value, key:value}
- 适用场景:前台只需要部分字段数据时
- 用法:
查询指定字段值(values_list ())
- 用法:
User.objects.all().values_list("id", "username")
- 说明:返回包含指定字段值的元组集合,如
[(1, '路飞123'), (2, '乔巴')]
- 适用场景:只需要字段值而不需要对象属性时
- 用法:
排除条件查询(exclude ())
- 用法:
User.objects.exclude(id=2)
- 说明:返回除满足条件之外的所有记录
- 用法:
排序查询(order_by ())
- 基本用法:
User.objects.order_by("sal")
(默认升序) - 降序查询:
User.objects.order_by("-id")
(字段前加负号) - 多字段排序:
User.objects.order_by("sal", "-id")
(先按 sal 升序,相同则按 id 降序)
- 基本用法:
反转排序(reverse ())
- 用法:
User.objects.order_by("-id").reverse()
- 说明:对已排序的结果进行反转,需先使用 order_by ()
- 用法:
统计记录数(count ())
- 用法:
User.objects.all().count()
- 说明:返回查询结果的记录总数
- 用法:
获取首尾记录(first ()/last ())
- 用法:
User.objects.all().last()
或User.objects.all().first()
- 说明:返回查询结果中的第一条或最后一条记录,结果为对象
- 用法:
判断记录是否存在(exists ())
- 用法:
User.objects.filter(id=2).exists()
- 说明:判断查询结果是否存在,返回布尔值(True/False)
- 用法:
组合示例
User.objects.filter(status=1).order_by("-create_time").values("id", "name")
。
下划线查询:
比较查询
属性__gt=值
:大于(>),如id__gt=1
表示id > 1
属性__gte=值
:大于等于(>=),如age__gte=18
表示age >= 18
属性__lt=值
:小于(<),如price__lt=100
表示price < 100
属性__lte=值
:小于等于(<=),如score__lte=60
表示score <= 60
范围查询
属性__range=[start, end]
:在指定范围内(包含边界),如id__range=[1,3]
等价于id >= 1 and id <= 3
成员查询
属性__in=[值1, 值2, ...]
:匹配列表中的任意值,如id__in=[1,3,5]
等价于id=1 or id=3 or id=5
模糊查询
属性__contains=值
:包含指定字符(区分英文大小写),如username__contains="路"
等价于like '%路%'
属性__icontains=值
:包含指定字符(不区分英文大小写),如username__icontains="lu"
会匹配 "LU"、"Lu" 等属性__startswith=值
:以指定字符开头,如name__startswith="张"
等价于like '张%'
属性__endswith=值
:以指定字符结尾,如email__endswith="qq.com"
等价于like '%qq.com'
多条件组合
可以将多个下划线查询条件组合使用,实现复杂过滤:# 查询 id>1 且 id<=3 的用户 User.objects.filter(id__gt=1, id__lte=3)
二,多表查询
一对多
1、基础概念
- 一对多关系:一个出版社(一方)可关联多本图书(多方),图书通过外键(如
press = models.ForeignKey(Press, ...)
)关联到出版社。 - 查询方向:分为 “从多方查一方”“从一方查多方”“反向查询” 三类。
2、从多方(Book)查询一方(Press)
即通过图书查询其所属的出版社,利用外键字段直接访问关联的一方对象。
.直接获取关联的一方对象
# 获取id=2的图书,再访问其关联的出版社
book = Book.objects.get(id=2)
print(book.press) # 输出该图书对应的Press对象
print(book.press.pressname) # 访问出版社的字段(如出版社名称)
- 原理:通过多方模型的外键字段(
press
)直接获取关联的一方实例。
3、从一方(Press)查询多方(Book)
即通过出版社查询其所有关联的图书,有两种常用方式:
3.1通过外键 ID 查询(直接条件过滤)
# 查询出版社id=1对应的所有图书(直接使用外键的id字段)
booklist = Book.objects.filter(press_id=1)
- 说明:
press_id
是外键在数据库中的实际字段名(默认外键字段名_id
),直接通过 ID 过滤多方数据。
3.2 跨表联查(通过一方字段过滤)
利用外键字段__一方字段
的语法实现跨表查询,生成一条联表 SQL。
# 查询名称包含“清华”的出版社对应的所有图书
booklist = Book.objects.filter(press__pressname__contains="清华")
- 语法:
外键字段(press)__一方字段(pressname)
,支持嵌套(如__contains
模糊查询)。 - 优势:仅执行一次 SQL 查询,效率更高。
3.3 先查一方对象,再通过 ID 关联(多 SQL 查询)
# 先查询出版社对象,再通过其ID过滤图书(生成两条SQL)
press = Press.objects.get(pressname="清华大学出版社")
booklist = Book.objects.filter(press_id=press.id)
- 说明:分两步查询,先获取一方实例,再用其 ID 作为条件查询多方,适用于需要先处理一方对象的场景。
四、反向查询(从一方主动查询多方)
一方模型本身不直接存储外键,需通过 “反向关联” 语法查询多方数据,有两种方式:
1. 默认反向关联(多方类名小写_set
)
Django 自动生成的反向关联属性,格式为多方模型类名小写_set
。
# 查询“清华大学出版社”关联的所有图书
press = Press.objects.get(pressname="清华大学出版社")
booklist = press.book_set.all() # book_set是自动生成的反向关联属性
- 说明:
book_set
本质是一个管理器(Manager),支持all()
、filter()
等查询方法。
2. 自定义反向关联(related_name
)
在定义外键时通过related_name
指定反向关联名称,替代默认的book_set
。
# 定义外键时指定related_name
class Book(models.Model):press = models.ForeignKey(Press, on_delete=models.CASCADE, related_name="books") # 自定义反向名称为books# 使用自定义名称查询
press = Press.objects.get(pressname="清华大学出版社")
booklist = press.books.all() # 直接使用related_name(books)访问
- 优势:更直观,推荐使用。
五、跨表查询指定字段(values ())
通过values()
指定多方和一方的字段,返回包含跨表字段的字典。
# 查询所有图书的名称及其对应出版社的名称
booklist = Book.objects.all().values("bookname", "press__pressname")
# 结果示例:[{'bookname': '鹿鼎记', 'press__pressname': '人民文学出版社'}, ...]
- 语法:
values(多方字段, 外键字段__一方字段)
,适合只需要部分字段的场景。
六、通过多方字段反向查询一方
即根据多方的条件查询对应的一方(如 “查询《鹿鼎记》所属的出版社”)。
# 查询图书名称为“鹿鼎记”的图书对应的出版社
press = Press.objects.get(books__bookname="鹿鼎记") # books是related_name
- 原理:利用反向关联名称(
books
)+ 多方字段(bookname
)过滤一方数据。
总结
查询场景 | 核心语法 / 方法 | 示例 |
---|---|---|
多方查一方 | 多方对象。外键字段 | book.press.pressname |
一方查多方(ID 过滤) | Book.objects.filter(press_id=一方ID) | Book.objects.filter(press_id=1) |
一方查多方(跨表联查) | Book.objects.filter(外键__一方字段=条件) | filter(press__pressname__contains="清华") |
反向查询(默认) | 一方对象。多方类名小写_set.all () | press.book_set.all() |
反向查询(自定义) | 一方对象.related_name.all () | press.books.all() (需定义 related_name) |
跨表指定字段 | values(多方字段, 外键__一方字段) | values("bookname", "press__pres |
三、聚合查询
用于对数据进行统计计算(如求和、平均值等),核心是对一组数据进行汇总分析。
1. 核心特点
- 作用:对查询集(QuerySet)进行统计计算,返回汇总结果。
- 常用函数:
Sum
、Avg
、Max
、Min
、Count
(需从django.db.models
导入)。 - 常用方法:
aggregate()
:返回整体统计结果(字典)。annotate()
:为每个对象添加聚合字段(分组统计)。
2. 示例
from django.db.models import Sum, Avg, Count
from .models import Product# 1. 整体统计(aggregate)
# 计算所有商品的平均价格、总库存
result = Product.objects.aggregate(avg_price=Avg('price'),total_stock=Sum('stock')
)
# 结果:{'avg_price': 89.5, 'total_stock': 500}# 2. 分组统计(annotate)
# 统计每个分类下的商品数量
from .models import Category
categories = Category.objects.annotate(product_count=Count('product') # product是Category与Product的关联名称
)
for c in categories:print(f"分类{c.name}有{c.product_count}个商品")
四、F 表达式
用于引用模型字段的值,实现字段间的直接计算或比较,避免 Python 层面的二次处理。
1. 核心特点
- 作用:在数据库层面操作字段值,无需将数据加载到内存。
- 适用场景:字段间比较、基于字段值的更新、计算等。
- 导入:
from django.db.models import F
。
2. 示例
from django.db.models import F
from .models import Product# 1. 字段间比较查询
# 查询库存大于销量的商品(stock > sales)
products = Product.objects.filter(stock__gt=F('sales'))# 2. 基于字段值的更新
# 所有商品价格上涨10%(price = price * 1.1)
Product.objects.update(price=F('price') * 1.1)# 3. 字段计算作为新值
# 查询时添加“库存余量”字段(stock - sales)
products = Product.objects.annotate(remaining=F('stock') - F('sales')
).values('name', 'remaining')
五、Q 表达式
用于构建复杂的查询条件,支持逻辑运算符(与、或、非)组合多个查询条件。
1. 核心特点
- 作用:处理多条件的逻辑组合(如
OR
关系),弥补filter()
默认AND
逻辑的不足。 - 运算符:
Q(a) | Q(b)
:逻辑或(a 或 b)Q(a) & Q(b)
:逻辑与(a 且 b,等价于filter(a, b)
)~Q(a)
:逻辑非(非 a)
- 导入:
from django.db.models import Q
。
2. 示例
from django.db.models import Q
from .models import Product# 1. 逻辑或(OR)查询
# 查询价格低于50元 或 库存大于100的商品
products = Product.objects.filter(Q(price__lt=50) | Q(stock__gt=100)
)# 2. 组合逻辑(与+或)
# 查询(价格>100且销量>50) 或 分类为"数码"的商品
products = Product.objects.filter(Q(price__gt=100) & Q(sales__gt=50) | Q(category__name="数码")
)# 3. 逻辑非(NOT)
# 查询不属"数码"分类的商品
products = Product.objects.filter(~Q(category__name="数码"))
六、三者的结合使用
聚合查询、F、Q 表达式可以灵活组合,解决更复杂的需求。
1. 聚合 + F 表达式
# 计算每个商品的“总价”(price * stock),并统计所有商品的总价之和
from django.db.models import Sum, Ftotal_value = Product.objects.aggregate(total=Sum(F('price') * F('stock')) # 先计算单商品总价,再求和
)
# 结果:{'total': 25600.0}(所有商品库存总价值)
2. 聚合 + Q 表达式
# 统计“价格>100或销量>50”的商品总数
from django.db.models import Count, Qcount = Product.objects.filter(Q(price__gt=100) | Q(sales__gt=50)
).aggregate(total=Count('id'))
# 结果:{'total': 30}
3. 三者结合
# 统计每个分类中“库存>销量”的商品数量
categories = Category.objects.annotate(valid_count=Count('product', filter=Q(product__stock__gt=F('product__sales')) # 过滤条件:库存>销量)
)
七、核心区别与应用场景
工具 | 核心作用 | 典型场景 |
---|---|---|
聚合查询 | 数据统计(求和、平均值等) | 计算总数、分组统计、跨表汇总 |
F 表达式 | 字段间操作(比较、计算) | 字段比较、基于原字段更新、动态计算字段值 |
Q 表达式 | 组合复杂查询条件(逻辑运算) | 实现 OR、NOT 逻辑,多条件灵活组合 |