【高级秘籍】Django ORM查询全解析:15个必会技巧提升性能10倍
前言:ORM查询效率决定应用性能上限
在Django开发中,ORM(对象关系映射)是连接Python代码与数据库的桥梁。虽然ORM简化了数据库操作,使我们能够用Python语法代替原生SQL,但掌握其高级查询技巧却是构建高性能应用的关键。据统计,超过80%的Django应用性能瓶颈出现在数据库查询上,而大多数开发者只使用了Django ORM不到30%的功能。本文将深入探讨Django ORM的高级查询技巧,帮助你充分释放数据库性能潜力。
1. 基础查询技巧回顾
在深入高级技巧前,让我们快速回顾一些基础查询方法:
# 基本查询操作
products = Product.objects.all() # 获取所有产品
active_products = Product.objects.filter(is_active=True) # 过滤活跃产品
new_product = Product.objects.get(id=1) # 获取单个产品
cheap_products = Product.objects.filter(price__lt=100) # 比较查询
ordered_products = Product.objects.order_by('-created_at') # 排序
limited_products = Product.objects.all()[:10] # 切片限制
# 基本关联查询
category_products = Product.objects.filter(category__name='Electronics') # 关联过滤
这些基础查询对于简单应用已经足够,但对于复杂场景和高性能要求,我们需要更强大的技巧。
2. 复杂过滤条件构建
2.1 使用Q对象组合查询条件
Q
对象允许我们构建复杂的查询条件,支持AND、OR和NOT操作:
from django.db.models import Q
# OR条件:查找name包含"phone"或price小于500的产品
results = Product.objects.filter(
Q(name__icontains='phone') | Q(price__lt=500)
)
# AND条件:查找name包含"phone"且price小于500的产品
results = Product.objects.filter(
Q(name__icontains='phone') & Q(price__lt=500)
)
# NOT条件:查找name包含"phone"但category不是"Electronics"的产品
results = Product.objects.filter(
Q(name__icontains='phone') & ~Q(category__name='Electronics')
)
# 组合复杂条件:(A OR B) AND (C OR D)
results = Product.objects.filter(
(Q(name__icontains='phone') | Q(name__icontains='tablet')) &
(Q(price__lt=500) | Q(is_featured=True))
)
2.2 使用F对象进行字段间比较
F
对象允许我们在查询中引用字段值,进行字段间比较或数据库级运算:
from django.db.models import F
# 查找销量大于库存的产品
products = Product.objects.filter(sold_count__gt=F('stock'))
# 查找利润率大于20%的产品(price > cost * 1.2)
products = Product.objects.filter(price__gt=F('cost') * 1.2)
# 更新所有产品价格上涨10%
Product.objects.update(price=F('price') * 1.1)
# 更新多个字段,并保持字段间关系
Product.objects.update(
price=F('price') * 1.1,
sale_price=F('price') * 0.9 # 此处price是更新前的值
)
2.3 动态构建查询条件
在实际项目中,我们经常需要根据用户输入动态构建查询条件:
def search_products(request):
query = Product.objects.all()
# 从请求参数构建过滤条件
if 'category' in request.GET:
query = query.filter(category__slug=request.GET['category'])
if 'min_price' in request.GET:
query = query.filter(price__gte=request.GET['min_price'])
if 'max_price' in request.GET:
query = query.filter(price__lte=request.GET['max_price'])
if 'search' in request.GET:
# 构建OR条件搜索多个字段
search_query = Q(name__icontains=request.GET['search']) | \
Q(description__icontains=request.GET['search'])
query = query.filter(search_query)
# 处理排序
sort = request.GET.get('sort', 'name')
if sort.startswith('-'):
sort_field = sort[1:]
query = query.order_by(F(sort_field).desc(nulls_last=True))
else:
query = query.order_by(F(sort).asc(nulls_last=True))
return query
2.4 复杂查询的最佳实践
- 使用查询表达式而非Python过滤:将过滤逻辑放在数据库层而非Python层
- 构建可重用的查询部件:将常用查询条件封装为方法
- 使用条件表达式避免多次查询:
from django.db.models import Case, When, Value, IntegerField
# 根据条件分类产品
products = Product.objects.annotate(
product_class=Case(
When(price__lt=100, then=Value(1)), # 经济型
When(price__lt=500, then=Value(2)), # 中端
When(price__lt=1000, then=Value(3)), # 高端
default=Value(4), # 豪华
output_field=IntegerField()
)
)
3. 高级关联查询技巧
3.1 优化关联查询:select_related与prefetch_related
减少数据库查询次数是提升性能的关键:
# 问题:N+1查询问题
orders = Order.objects.all() # 1次查询获取所有订单
for order in orders:
customer = order.customer # 每个订单额外1次查询获取客户
# 总共N+1次查询!
# 解决方案1:select_related() - 用于ForeignKey和OneToOneField
orders = Order.objects.select_related('customer') # 1次查询同时获取订单和客户
# 解决方案2:prefetch_related() - 用于ManyToMany和反向关系
orders = Order.objects.prefetch_related('items') # 2次查询高效获取所有订单项
# 组合使用
orders = Order.objects.select_related('customer').prefetch_related('items')
# 嵌套使用
orders = Order.objects.select_related('customer').prefetch_related(
'items__product' # 预取订单项的产品
)
3.2 高级预取:Prefetch对象
使用Prefetch
对象对预取的数据进行过滤和定制:
from django.db.models import Prefetch
# 只预取活跃产品
categories = Category.objects.prefetch_related(
Prefetch('products', queryset=Product.objects.filter(is_active=True))
)
# 预取并指定结果名称
users = User.objects.prefetch_related(
Prefetch('orders',
queryset=Order.objects.filter(status='completed'),
to_attr='completed_orders')
)
# 使用预取结果
for user in users:
completed_count = len(user.completed_orders) # 不触发额外查询
# 预取后再过滤
recent_orders = Order.objects.prefetch_related(
Prefetch('items',
queryset=OrderItem.objects.select_related('product'))
).filter(created_at__gte=one_week_ago)
3.3 使用Subquery和Exists进行子查询
Django 2.0+引入的Subquery
和Exists
允许更高效的子查询:
from django.db.models import Subquery, OuterRef, Exists
# 查找有库存产品的订单
in_stock_products = Product.objects.filter(stock__gt=0)
orders_with_in_stock = Order.objects.filter(
Exists(OrderItem.objects.filter(
order=OuterRef('pk'),
product__in=in_stock_products
))
)
# 添加最近评论到产品查询
recent_comment = Comment.objects.filter(
product=OuterRef('pk')
).order_by('-created_at').values('text')[:1]
products = Product.objects.annotate(
recent_comment=Subquery(recent_comment)
)
# 添加子查询计数
from django.db.models import Count, Subquery
# 查找订单数大于5的客户
customer_order_count = Order.objects.filter(
customer=OuterRef('pk')
).values('customer').annotate(
count=Count('*')
).values('count')
customers = Customer.objects.annotate(
order_count=Subquery(customer_order_count)
).filter(order_count__gt=5)
4. 聚合与注解:数据计算
4.1 基础聚合函数
Django提供多种聚合函数用于计算数据:
from django.db.models import Count, Sum, Avg, Min, Max
# 计算总数
product_count = Product.objects.count()
# 计算多个聚合值
stats = Product.objects.aggregate(
count=Count('id'),
avg_price=Avg('price'),
max_price=Max('price'),
min_price=Min('price'),
total_stock=Sum('stock')
)
# 结果: {'count': 100, 'avg_price': 299.99, 'max_price': 999.99, ...}
# 分组聚合
category_stats = Product.objects.values('category__name').annotate(
count=Count('id'),
avg_price=Avg('price')
).order_by('-count')
4.2 高级注解与条件聚合
使用annotate
、Case/When
、Window
函数进行复杂计算:
from django.db.models import Case, When, Value, IntegerField, Count, F, Window
from django.db.models.functions import DenseRank
# 条件注解:计算不同价格范围的产品数量
price_ranges = Product.objects.aggregate(
low=Count(Case(When(price__lt=100, then=1))),
medium=Count(Case(When(price__gte=100, price__lt=500, then=1))),
high=Count(Case(When(price__gte=500, then=1)))
)
# 添加排名信息
products = Product.objects.annotate(
rank=Window(
expression=DenseRank(),
order_by=F('price').desc()
)
).filter(rank__lte=10) # 获取价格前10的产品
# 复杂注解:计算每个产品销售百分比
from django.db.models.functions import Cast
from django.db.models import FloatField
products = Product.objects.annotate(
sold_percent=Case(
When(stock_total__gt=0,
then=Cast(F('sold_count') * 100.0 / F('stock_total'),
output_field=FloatField())),
default=Value(0.0),
output_field=FloatField()
)
)
4.3 分组查询与高级统计
# 按月统计销售额
from django.db.models.functions import TruncMonth
monthly_sales = Order.objects.annotate(
month=TruncMonth('created_at')
).values('month').annotate(
total=Sum('total_amount'),
count=Count('id')
).order_by('month')
# 计算滚动平均值
from django.db.models.functions import Lag
products = Product.objects.annotate(
prev_price=Window(
expression=Lag('price', offset=1),
partition_by=[F('category_id')],
order_by=F('id').asc()
),
price_change=F('price') - F('prev_price')
)
5. 原生SQL与高级查询操作
5.1 使用raw()执行原生SQL
当ORM无法满足需求时,可以使用原生SQL:
# 简单原生查询
products = Product.objects.raw(
'SELECT id, name, price FROM app_product WHERE price > %s',
[100]
)
# 复杂原生查询
products = Product.objects.raw('''
SELECT p.id, p.name, p.price,
COUNT(o.id) as order_count
FROM app_product p
LEFT JOIN app_orderitem oi ON oi.product_id = p.id
LEFT JOIN app_order o ON oi.order_id = o.id
WHERE p.is_active = 1
GROUP BY p.id
HAVING COUNT(o.id) > 0
ORDER BY order_count DESC
''')
# 访问结果
for product in products:
print(f"{product.name}: {product.order_count}")
5.2 使用extra()添加自定义SQL
extra()
允许扩展ORM查询:
# 添加自定义选择字段
products = Product.objects.extra(
select={'discounted_price': 'price * 0.9'}
)
# 添加自定义WHERE条件
products = Product.objects.extra(
where=['price > cost * 1.2']
)
# 添加自定义ORDER BY
products = Product.objects.extra(
order_by=['-price * sold_count'] # 按总销售额排序
)
5.3 使用SQL函数和表达式
Django提供多种内置函数用于复杂计算:
from django.db.models.functions import Concat, Lower, Upper, Length, Now
from django.db.models import Value, CharField, ExpressionWrapper, F, DecimalField
# 字符串操作
products = Product.objects.annotate(
name_upper=Upper('name'),
name_length=Length('name'),
full_title=Concat(
'category__name', Value(' - '), 'name',
output_field=CharField()
)
)
# 日期时间计算
from django.db.models.functions import Extract
from datetime import timedelta
orders = Order.objects.annotate(
month=Extract('created_at', 'month'),
year=Extract('created_at', 'year')
).filter(
created_at__gt=Now() - timedelta(days=30)
)
# 复杂数学计算
products = Product.objects.annotate(
profit=ExpressionWrapper(
F('price') - F('cost'),
output_field=DecimalField(max_digits=10, decimal_places=2)
),
profit_margin=ExpressionWrapper(
(F('price') - F('cost')) * 100 / F('price'),
output_field=DecimalField(max_digits=5, decimal_places=2)
)
)
6. 查询优化技巧
6.1 性能分析与调试
分析查询性能的工具和技巧:
# 1. 使用explain()查看查询计划(Django 3.0+)
queryset = Product.objects.filter(price__gt=100)
print(queryset.explain())
# 2. 使用debug打印实际执行的SQL
from django.db import connection
queryset = Product.objects.filter(price__gt=100)
print(queryset.query) # 打印查询SQL
# 查看所有执行的查询
for query in connection.queries:
print(f"SQL: {query['sql']}")
print(f"Time: {query['time']}s")
# 3. 使用django-debug-toolbar
# 安装django-debug-toolbar,查看Web界面显示的SQL分析
6.2 查询优化核心技巧
# 1. 只获取需要的字段
products = Product.objects.values('id', 'name', 'price') # 比获取整个对象快
# 或
products = Product.objects.only('id', 'name', 'price')
# 2. 延迟加载很少使用的大字段
products = Product.objects.defer('description', 'metadata')
# 3. 使用索引
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True) # 添加索引
price = models.DecimalField(max_digits=10, decimal_places=2, db_index=True)
class Meta:
indexes = [
models.Index(fields=['name', 'price']), # 复合索引
]
# 4. 批量获取避免大量查询
# 而不是:
for id in product_ids:
product = Product.objects.get(id=id) # BAD: N次查询
# 使用:
products = Product.objects.filter(id__in=product_ids) # GOOD: 1次查询
# 5. 批量创建和更新
Product.objects.bulk_create([
Product(name='Item 1', price=10.0),
Product(name='Item 2', price=20.0),
# ...more items
], batch_size=100) # 分批处理以减少内存使用
# 批量更新
Product.objects.filter(category=cat).update(price=F('price') * 1.1)
6.3 避免常见的性能陷阱
# 1. 避免在循环中查询
# 不要这样做:
for product in products:
categories = product.categories.all() # N个查询!
# 正确做法:
products = Product.objects.prefetch_related('categories')
for product in products:
categories = product.categories.all() # 使用预加载的数据
# 2. 避免计算字段滥用
# 这会导致所有数据都被加载到Python中计算
products = Product.objects.all()
sorted_products = sorted(products, key=lambda p: p.price * p.sold_count)
# 正确做法:
from django.db.models import F
products = Product.objects.annotate(
total_sales=F('price') * F('sold_count')
).order_by('-total_sales')
# 3. 使用迭代器处理大量查询结果
# 不要:
all_products = list(Product.objects.all()) # 一次加载所有数据到内存
# 而是:
for product in Product.objects.iterator(): # 批量加载数据
process_product(product)
7. 事务管理与并发控制
7.1 使用事务确保数据一致性
from django.db import transaction
# 方法1: 装饰器
@transaction.atomic
def transfer_funds(from_account, to_account, amount):
from_account.balance -= amount
from_account.save()
to_account.balance += amount
to_account.save()
# 方法2: 上下文管理器
def complex_operation():
with transaction.atomic():
# 多个操作作为一个原子单元
order = Order.objects.create(customer=customer, total=total)
for item in items:
OrderItem.objects.create(order=order, product=item.product)
# 如果任何一步失败,整个事务回滚
# 保存点 - 允许部分回滚
def complex_process():
with transaction.atomic():
order = Order.objects.create(customer=customer)
# 创建保存点
sid = transaction.savepoint()
try:
# 尝试扣减库存
for item in cart:
product = Product.objects.select_for_update().get(id=item.product_id)
if product.stock < item.quantity:
raise Exception("库存不足")
product.stock -= item.quantity
product.save()
OrderItem.objects.create(order=order, product=product)
except Exception as e:
# 回滚到保存点
transaction.savepoint_rollback(sid)
# 处理订单但不扣减库存
order.status = 'pending_stock'
order.save()
else:
# 提交保存点
transaction.savepoint_commit(sid)
7.2 处理并发控制与锁
# 悲观锁 - 使用select_for_update()锁定行
def process_order(order_id, user_id):
with transaction.atomic():
# 锁定订单行,阻止其他事务修改
order = Order.objects.select_for_update().get(id=order_id)
# 安全进行操作,确信没有其他事务同时修改此行
order.status = 'processing'
order.processed_by = user_id
order.save()
# 乐观锁 - 使用版本字段检测冲突
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField(default=0)
version = models.IntegerField(default=0) # 版本字段
def update_product(product_id, new_data):
# 乐观锁实现
while True:
try:
product = Product.objects.get(id=product_id)
current_version = product.version
# 更新数据
for key, value in new_data.items():
setattr(product, key, value)
# 增加版本号
product.version = current_version + 1
# 尝试保存,确保版本未更改
rows_updated = Product.objects.filter(
id=product_id,
version=current_version
).update(
**new_data,
version=current_version + 1
)
if rows_updated == 0:
# 版本已更改,重试
continue
return product
except Product.DoesNotExist:
raise Exception("产品不存在")
8. 高级查询案例实战
8.1 电商系统的推荐引擎查询
def get_product_recommendations(product_id, limit=5):
"""基于共同购买行为的产品推荐"""
# 找出购买了此产品的订单
target_orders = Order.objects.filter(
items__product_id=product_id
).values_list('id', flat=True)
# 找出这些订单中购买的其他产品
from django.db.models import Count
recommended_products = Product.objects.filter(
orderitem__order_id__in=target_orders # 同一订单中的产品
).exclude(
id=product_id # 排除当前产品
).annotate(
purchase_count=Count('orderitem') # 统计共同购买次数
).order_by(
'-purchase_count' # 按共同购买次数排序
)[:limit]
return recommended_products
8.2 复杂报表查询
def generate_sales_report(start_date, end_date):
"""生成销售报表,包含多维度分析"""
from django.db.models.functions import TruncDay
from django.db.models import Sum, Count, Avg, F, Window
# 基础查询 - 按日汇总销售
daily_sales = Order.objects.filter(
created_at__range=(start_date, end_date),
status='completed'
).annotate(
day=TruncDay('created_at')
).values('day').annotate(
orders=Count('id'),
revenue=Sum('total_amount'),
avg_order_value=Avg('total_amount')
).order_by('day')
# 按类别统计销售
category_sales = OrderItem.objects.filter(
order__created_at__range=(start_date, end_date),
order__status='completed'
).values(
'product__category__name'
).annotate(
items_sold=Sum('quantity'),
revenue=Sum(F('price') * F('quantity'))
).order_by('-revenue')
# 计算累计销售额和环比增长
with_running_total = daily_sales.annotate(
running_total=Window(
expression=Sum('revenue'),
order_by=F('day').asc()
)
)
return {
'daily_sales': list(daily_sales),
'category_sales': list(category_sales),
'totals': {
'orders': sum(day['orders'] for day in daily_sales),
'revenue': sum(day['revenue'] for day in daily_sales),
}
}
8.3 高效分页与无限滚动实现
def paginate_products(request, page_size=20):
"""高效分页实现,支持各种过滤和排序"""
# 获取查询参数
page = int(request.GET.get('page', 1))
sort_by = request.GET.get('sort', 'created_at')
ascending = request.GET.get('asc', 'false').lower() == 'true'
# 基础查询
query = Product.objects.filter(is_active=True)
# 添加过滤条件
category = request.GET.get('category')
if category:
query = query.filter(category__slug=category)
price_min = request.GET.get('price_min')
if price_min:
query = query.filter(price__gte=price_min)
price_max = request.GET.get('price_max')
if price_max:
query = query.filter(price__lte=price_max)
# 计算总数(使用缓存减少重复计算)
from django.core.cache import cache
cache_key = f"product_count:{category}:{price_min}:{price_max}"
total_count = cache.get(cache_key)
if total_count is None:
total_count = query.count()
cache.set(cache_key, total_count, 3600) # 缓存1小时
# 添加排序
if sort_by:
order_field = sort_by if ascending else f"-{sort_by}"
query = query.order_by(order_field)
# 使用高效分页
# 对于大数据集,使用基于游标的分页,避免COUNT查询
if page > 1 and request.GET.get('cursor'):
cursor_value = request.GET.get('cursor')
lookup = '__gte' if ascending else '__lte'
filter_kwargs = {f"{sort_by}{lookup}": cursor_value}
query = query.filter(**filter_kwargs)
# 选择需要的字段,减少数据传输
query = query.values('id', 'name', 'price', 'image_url', 'category__name')
# 取一页数据
results = list(query[:page_size+1]) # 多取一个用于检查是否有下一页
has_next = len(results) > page_size
if has_next:
results = results[:page_size] # 去掉多取的那一个
# 返回结果
next_cursor = results[-1][sort_by] if results and has_next else None
return {
'results': results,
'total': total_count,
'page': page,
'has_next': has_next,
'next_cursor': next_cursor
}
9. 高级ORM模式
9.1 实现软删除功能
class SoftDeleteManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(deleted_at__isnull=True)
class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(null=True, blank=True)
objects = SoftDeleteManager() # 默认管理器,只返回未删除的对象
all_objects = models.Manager() # 返回所有对象,包括已删除的
def delete(self, hard=False, **kwargs):
if hard:
return super().delete(**kwargs)
else:
self.deleted_at = timezone.now()
self.save()
def restore(self):
self.deleted_at = None
self.save()
class Meta:
abstract = True
# 使用软删除模型
class Product(SoftDeleteModel):
name = models.CharField(max_length=100)
# ...
# 使用软删除
product = Product.objects.get(id=1)
product.delete() # 软删除,设置deleted_at字段
# 硬删除
product.delete(hard=True) # 永久删除记录
# 查询包括已删除的记录
all_products = Product.all_objects.all()
# 恢复已删除的记录
deleted_product = Product.all_objects.get(id=1)
deleted_product.restore()
9.2 封装复杂查询逻辑
class ProductQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def featured(self):
return self.filter(is_featured=True)
def in_stock(self):
return self.filter(stock__gt=0)
def on_sale(self):
return self.filter(sale_price__lt=models.F('price'))
def price_range(self, min_price=None, max_price=None):
queryset = self
if min_price:
queryset = queryset.filter(price__gte=min_price)
if max_price:
queryset = queryset.filter(price__lte=max_price)
return queryset
def search(self, query):
if not query:
return self
return self.filter(
models.Q(name__icontains=query) |
models.Q(description__icontains=query) |
models.Q(category__name__icontains=query)
)
class ProductManager(models.Manager):
def get_queryset(self):
return ProductQuerySet(self.model, using=self._db)
def active(self):
return self.get_queryset().active()
def featured(self):
return self.get_queryset().active().featured()
def bestsellers(self, limit=10):
return self.get_queryset().active().annotate(
sold=models.Sum('orderitem__quantity')
).order_by('-sold')[:limit]
class Product(models.Model):
# ... 字段定义
objects = ProductManager()
# ... 其他方法
# 使用链式查询API
products = Product.objects.active().in_stock().price_range(10, 100).search('phone')
featured_deals = Product.objects.active().featured().on_sale()
9.3 实现查询缓存机制
from django.core.cache import cache
from functools import wraps
def cached_queryset(timeout=3600):
"""为查询集添加缓存的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存键
cache_key = f"{func.__name__}:{hash(str(args))}:{hash(str(kwargs))}"
# 尝试从缓存获取
cached_result = cache.get(cache_key)
if cached_result is not None:
return cached_result
# 执行查询
result = func(*args, **kwargs)
# 缓存结果
cache.set(cache_key, result, timeout)
return result
return wrapper
return decorator
class ProductService:
@cached_queryset(timeout=1800) # 30分钟缓存
def get_featured_products(self, limit=10):
return list(
Product.objects.filter(
is_active=True,
is_featured=True
).select_related(
'category'
).order_by('-created_at')[:limit]
)
@cached_queryset(timeout=3600) # 1小时缓存
def get_category_products(self, category_slug, page=1, page_size=20):
offset = (page - 1) * page_size
products = Product.objects.filter(
is_active=True,
category__slug=category_slug
).select_related(
'category'
).order_by('-created_at')[offset:offset+page_size]
return list(products)
def invalidate_cache(self, product=None, category=None):
"""当数据更新时,使相关缓存失效"""
# 实现缓存失效逻辑
if product:
# 使产品相关缓存失效
cache.delete(f"get_featured_products:{hash(str(()))}:{hash(str({}))}")
if product.category:
cache.delete(f"get_category_products:{hash(str((product.category.slug,)))}:{hash(str({}))}")
10. Django ORM的未来展望与最佳实践
10.1 Django ORM的最新特性与趋势
Django近几个版本增加了许多新特性:
- Django 3.2: 函数式索引、UniqueConstraint的条件约束
- Django 4.0: 异步ORM查询、defer()的逆运算-only()
- Django 4.1: 多字段db_default、表名前缀支持
- Django 4.2: 模型更好的表达式支持、注解的输出字段名控制
未来趋势:
- 更强的异步支持
- 更丰富的数据库特性支持(如JSON字段操作)
- 更灵活的查询表达式
- 更好的性能优化工具
10.2 ORM查询的最佳实践总结
- 了解生成的SQL:总是了解你的ORM查询生成的SQL
- 减少查询次数:使用select_related和prefetch_related减少查询
- 优先使用数据库操作:使用数据库函数而非Python处理大量数据
- 恰当使用索引:为常用查询条件创建索引,但避免过多索引
- 批量操作:使用bulk_create、bulk_update和迭代器处理大量数据
- 事务控制:适当使用事务保证数据一致性
- 缓存结果:缓存重复查询的结果
- 编写可测试查询:保持查询逻辑的可测试性
- 避免反模式:避免N+1查询和其他常见性能陷阱
- 持续监控:使用工具监控查询性能
10.3 真实项目中的性能优化案例
# 案例1: 优化大量关联数据的显示
# 优化前:
def dashboard(request):
orders = Order.objects.filter(user=request.user)
# 问题: 每个订单加载项目时都会发出单独查询
return render(request, 'dashboard.html', {'orders': orders})
# 优化后:
def dashboard(request):
orders = Order.objects.filter(
user=request.user
).prefetch_related(
Prefetch(
'items',
queryset=OrderItem.objects.select_related('product')
)
)
return render(request, 'dashboard.html', {'orders': orders})
# 性能提升:将数据库查询从N+1次减少到2次
# 案例2: 使用窗口函数优化排行榜
# 优化前:
def leaderboard(request):
users = User.objects.all()
# 为每个用户计算分数 - 很多查询!
for user in users:
user.score = calculate_score(user)
users = sorted(users, key=lambda u: u.score, reverse=True)
return render(request, 'leaderboard.html', {'users': users[:100]})
# 优化后:
def leaderboard(request):
users = User.objects.annotate(
score=Sum('activity__points'),
rank=Window(
expression=Rank(),
order_by=F('score').desc()
)
).filter(rank__lte=100).order_by('rank')
return render(request, 'leaderboard.html', {'users': users})
# 性能提升:将处理从Python移至数据库,查询次数从N+1减少到1次
总结
Django ORM是一个功能强大的工具,掌握其高级查询技巧可以显著提升应用性能和代码质量。本文介绍了15个必会技巧,从复杂过滤条件构建到高性能查询优化,从关联查询技巧到并发控制,希望能帮助你在实际项目中更好地应用Django ORM。
记住,高效的数据库查询是Django应用性能的关键所在。通过合理使用Django ORM提供的工具,你可以在保持代码可读性和维护性的同时,实现卓越的应用性能。
在下一篇文章中,我们将探讨Django表单处理与验证的最佳实践,敬请期待!