03【高级秘籍】Django ORM查询全解析:15个必会技巧提升性能10倍

【高级秘籍】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+引入的SubqueryExists允许更高效的子查询:

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 高级注解与条件聚合

使用annotateCase/WhenWindow函数进行复杂计算:

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: 模型更好的表达式支持、注解的输出字段名控制

未来趋势:

  1. 更强的异步支持
  2. 更丰富的数据库特性支持(如JSON字段操作)
  3. 更灵活的查询表达式
  4. 更好的性能优化工具

10.2 ORM查询的最佳实践总结

  1. 了解生成的SQL:总是了解你的ORM查询生成的SQL
  2. 减少查询次数:使用select_related和prefetch_related减少查询
  3. 优先使用数据库操作:使用数据库函数而非Python处理大量数据
  4. 恰当使用索引:为常用查询条件创建索引,但避免过多索引
  5. 批量操作:使用bulk_create、bulk_update和迭代器处理大量数据
  6. 事务控制:适当使用事务保证数据一致性
  7. 缓存结果:缓存重复查询的结果
  8. 编写可测试查询:保持查询逻辑的可测试性
  9. 避免反模式:避免N+1查询和其他常见性能陷阱
  10. 持续监控:使用工具监控查询性能

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表单处理与验证的最佳实践,敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Is code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值