目录
1,基础语法
python
# SELECT * FROM todo_demo_app_todo;
Todo.objects.all()
# SELECT id, name, is_done FROM todo_demo_app_todo;
Todo.objects.values('id', 'name', 'is_done')
# SELECT id, name FROM todo_demo_app_todo WHERE is_done = false;
Todo.objects.filter(is_done=False).values('id', 'name')
2,Where 条件查询
python
# 等于: WHERE name = 'task'
Todo.objects.filter(name='task')
# 不等于: WHERE name != 'task' 或 WHERE name <> 'task'
Todo.objects.exclude(name='task')
# 大于: WHERE priority > 'low'
Todo.objects.filter(priority__gt='low')
# 小于等于: WHERE created_at <= '2023-01-01'
Todo.objects.filter(created_at__lte='2023-01-01')
# AND: WHERE name LIKE '%work%' AND is_done = false
Todo.objects.filter(name__icontains='work', is_done=False)
# OR: WHERE name LIKE '%work%' OR priority = 'high'
from django.db.models import Q
Todo.objects.filter(
Q(name__icontains='work') | Q(priority='high')
)
# NOT: WHERE NOT (is_done = true)
Todo.objects.exclude(is_done=True)
3,模糊匹配查询
python
# LIKE '%value%': WHERE name LIKE '%task%'
Todo.objects.filter(name__icontains='task')
# LIKE 'value%': WHERE name LIKE 'task%'
Todo.objects.filter(name__istartswith='task')
# LIKE '%value': WHERE name LIKE '%end'
Todo.objects.filter(name__iendswith='end')
# 区分大小写版本
Todo.objects.filter(name__contains='Task') # 区分大小写
Todo.objects.filter(name__startswith='Task')
Todo.objects.filter(name__endswith='End')
4,范围查询 BETWEEN和IN
python
# BETWEEN: WHERE id BETWEEN 1 AND 10
Todo.objects.filter(id__range=[1, 10])
# IN: WHERE priority IN ('high', 'medium')
Todo.objects.filter(priority__in=['high', 'medium'])
# NOT IN: WHERE priority NOT IN ('low', 'none')
Todo.objects.exclude(priority__in=['low', 'none'])
5,空值处理
python
# IS NULL: WHERE description IS NULL
Todo.objects.filter(description__isnull=True)
# IS NOT NULL: WHERE description IS NOT NULL
Todo.objects.filter(description__isnull=False)
# 或者使用exclude
Todo.objects.exclude(description__isnull=True)
6,聚合函数
python
from django.db.models import Count, Sum, Avg, Max, Min
# COUNT(*): SELECT COUNT(*) FROM todo_demo_app_todo
total_count = Todo.objects.count()
# COUNT with GROUP BY: SELECT priority, COUNT(*) FROM todo group by priority
priority_counts = Todo.objects.values('priority').annotate(count=Count('id'))
# MAX/MIN: SELECT MAX(created_at) FROM todo
latest_todo = Todo.objects.aggregate(Max('created_at'))
earliest_todo = Todo.objects.aggregate(Min('created_at'))
# AVG: SELECT AVG(length(name)) FROM todo
avg_name_length = Todo.objects.extra(select={'name_len': 'LENGTH(name)'}).aggregate(Avg('name_len'))
7,分组查询
python
# 基础分组
# GROUP BY priority: SELECT priority, COUNT(*) FROM todo GROUP BY priority
grouped_by_priority = Todo.objects.values('priority').annotate(
count=Count('id')
)
# 多字段分组: GROUP BY user, priority
grouped_multi = Todo.objects.values('user', 'priority').annotate(
count=Count('id'),
avg_priority=Avg(Case(
When(priority='high', then=Value(3)),
When(priority='medium', then=Value(2)),
When(priority='low', then=Value(1)),
default=Value(0),
output_field=IntegerField()
))
)
# GROUP BY + HAVING: SELECT user, COUNT(*) FROM todo GROUP BY user HAVING COUNT(*) > 5
users_with_many_todos = Todo.objects.values('user').annotate(
count=Count('id')
).filter(count__gt=5)
8,排序
python
# ORDER BY created_at ASC
Todo.objects.order_by('created_at')
# ORDER BY created_at DESC
Todo.objects.order_by('-created_at')
# 多字段排序: ORDER BY priority DESC, created_at ASC
Todo.objects.order_by('-priority', 'created_at')
9,限制结果
python
# LIMIT 10: SELECT * FROM todo LIMIT 10
first_ten = Todo.objects.all()[:10]
# OFFSET 10 LIMIT 5: SELECT * FROM todo OFFSET 10 LIMIT 5
offset_few = Todo.objects.all()[10:15]
# 使用Paginator进行分页
from django.core.paginator import Paginator
paginator = Paginator(Todo.objects.all(), 20) # 每页20条
page_obj = paginator.get_page(1) # 第一页
10,关联查询
内连接:查询的两张表的交集,及表A和表B都拥有的数据;
左连接:左边表的数据会全部显示出来,右边只会列出 on 后面条件符合的数据拼接在左边表,无数据补 null;
右连接:右边表的数据会全部显示出来,左边只会列出on 后面条件符合的数据拼接在右边表,无数据补 null;
全连接:左右表都会显示出来,on后面条件符合的字段会重叠
python
# 内连接 INNER JOIN
# INNER JOIN: SELECT * FROM todo t JOIN user u ON t.user_id = u.id
todos_with_users = Todo.objects.select_related('user')
# 多重关联
todos_with_users_and_categories = Todo.objects.select_related('user', 'category')
# 左外连接(LEFT JOIN)
# LEFT JOIN: SELECT * FROM todo t LEFT JOIN comment c ON t.id = c.todo_id
todos_with_comments = Todo.objects.prefetch_related('comments')
# 预加载多个关系
complex_query = Todo.objects.select_related('user').prefetch_related('tags', 'comments')
11,子查询
Django ORM 子查询是指在一个查询(外查询)中嵌套另一个查询(内查询),将内查询的结果作为外查询的过滤条件或值,通过 Subquery``OuterRef;
python
from django.db.models import OuterRef, Subquery
# 获取每个用户的最新订单
latest_order_subquery = Order.objects.filter(user=OuterRef('pk')).order_by('-create_time').values('amount')[:1]
user_with_last_order_amount = User.objects.annotate(latest_order_amount=Subquery(latest_order_subquery))
for u in user_with_last_order_amount:
print(f"用户: {u.name}, 最新订单金额: {u.latest_order_amount}")
# filter(user=OuterRef('pk)) OuterRef('pk') 代表"外层查询(即 User 查询)中当前行的主键 ID"。意思是:"找出订单表Order中,user_id 等于当前用户 ID 的那些订单"。
# order_by('-create_time'):按下单时间倒序排列(最新的排在前面)
# values('amount')[:1]:只取第一条记录(也就是最新的那笔订单)的 amount 字段。
# annotate:这是 Django ORM 的"注解"功能。它不会修改数据库表结构,而是在查询结果集中为每个对象临时附加一个计算出的字段。
12,CASE WHEN 语句
作用:在查询订单时,动态生成一个包含中文状态描述的新字段,以便直接用于前端展示
python
from django.db.models import Case, When, Value
orders_with_status = Order.objects.annotate(
status_display=Case(
When(status='pending', then=Value('待支付')),
When(status='paid', then=Value('已支付')),
When(status='shipped', then=Value('已发货')),
When(status='completed', then=Value('已完成')),
When(status='cancelled', then=Value('已取消')),
default=Value('未知'),
output_field=CharField() # 告诉 django 这个计算结果是一个字符类型字段
)
)
for order in orders_with_status:
print(f"订单号: {order.order_no}")
print(f"原始状态: {order.status}")
print(f"显示状态: {order.status_display}")
# 输出结果
订单号: ORD202310010001
原始状态: paid
显示状态: 已支付
13,日期时间查询
python
from datetime import datetime, timedelta
# DATE(): WHERE DATE(created_at) = '2023-01-01'
today_todos = Todo.objects.filter(created_at__date=datetime.now().date())
# DATE_SUB(): WHERE created_at >= NOW() - INTERVAL 7 DAY
recent_todos = Todo.objects.filter(
created_at__gte=datetime.now() - timedelta(days=7)
)
# YEAR(), MONTH(), DAY(): WHERE YEAR(created_at) = 2023
year_2023_todos = Todo.objects.filter(created_at__year=2023)
month_todos = Todo.objects.filter(created_at__month=12)
day_todos = Todo.objects.filter(created_at__day=25)
14,性能优化
python
# 使用only()和defer()优化字段加载
# SELECT id, name FROM todo (只获取必要字段)
minimal_data = Todo.objects.only('id', 'name')
# 排除大字段
without_large_fields = Todo.objects.defer('description', 'content')
# 使用values/values_list获取原始数据
# 查询Todo表,返回一个包含元组的列表,类似 [(1,'代办事项1'),(2,'代办事项2')]
# flat = True 如果你只查询一个字段,可以使用这个属性返回一个扁平的值列表,而不是元组列表,类型[1,2,3,4]
raw_data = Todo.objects.values_list('id', 'name', flat=True)
values() 返回的是字典,DRF的serializer可以兼容字典输入,类似 [{'id': '...', 'name': ...}, ...]