系列:Python Web 框架
Django 安装与使用指南 覆盖了项目创建、ORM 基础、模板和后台管理等入门内容。本文继续深入,介绍 ORM 优化、Django 6.0 异步特性、中间件、信号、DRF 进阶、缓存策略和生产部署——这些是「把 Django 项目真正上线并扛住流量」必须掌握的内容。
本文基于 Django 6.0(2025 年 12 月发布)和 DRF >=3.14 的当前技术栈,部分示例可直接用于生产环境。
ORM 进阶与优化
Django ORM 是框架最强大的组件之一。用好它能避免大多数性能问题,用错则可能拖垮整个应用。
关联查询:消灭 N+1
最常见的性能陷阱是关联查询逐条执行:
# 反模式:N+1 查询for post in Post.objects.all(): print(post.author.name) # 每篇文章触发 1 次额外查询
# 推荐:用 select_related 做 JOINfor post in Post.objects.select_related("author"): print(post.author.name) # 无额外查询使用规则很简单:
| 方法 | 适用场景 | SQL 次数 |
|---|---|---|
select_related() | ForeignKey / OneToOne | 1 次 JOIN |
prefetch_related() | ManyToMany / 反向 ForeignKey | 2 次查询,Python 端拼接 |
两者可以链式组合:
posts = Post.objects.select_related("author").prefetch_related("tags", "comments")字段级查询优化
只取需要的字段,减少数据传输:
# 只需要标题和发布时间,不拉正文Post.objects.only("title", "pub_date")
# 只需要概要,跳过内容大字段Post.objects.defer("body")数据库端运算
把运算推到数据库层,而不是在 Python 中循环:
from django.db.models import F, Q, Count, Sum, Windowfrom django.db.models.functions import Rank
# F() 表达式:数据库端原子更新Product.objects.filter(stock__gt=0).update(stock=F("stock") - 1)
# Q() 对象:复杂条件组合Post.objects.filter( Q(status="published") & (Q(category="tech") | Q(tags__name="python")))
# annotate + 聚合:在数据库端完成统计from django.db.models import Count
categories = Category.objects.annotate(post_count=Count("post")).filter( post_count__gt=0)窗口函数(Window Functions)
在 QuerySet 上做排行、累计、前后对比,无需原始 SQL:
from django.db.models import Windowfrom django.db.models.functions import Rank
posts_with_rank = Post.objects.annotate( rank=Window( expression=Rank(), order_by=F("views").desc(), ))索引策略
不要对所有字段盲目加索引,只为高频查询条件建立:
class Post(models.Model): title = models.CharField(max_length=200, db_index=True) # 单列索引 status = models.CharField(max_length=20) pub_date = models.DateTimeField()
class Meta: indexes = [ models.Index(fields=["status", "-pub_date"], name="status_pub_idx"), # 联合索引 ]PostgreSQL 用户还可以考虑部分索引和表达式索引来覆盖更精准的查询场景。
查询预算中间件
生产环境中,一个简单办法是限制每个视图的查询数,超过阈值告警:
from django.db import connectionimport logging
logger = logging.getLogger("django.db")
class QueryBudgetMiddleware: def __init__(self, get_response): self.get_response = get_response
def __call__(self, request): before = len(connection.queries) response = self.get_response(request) after = len(connection.queries) count = after - before if count > 50: # 阈值可按视图定制 logger.warning( "query_budget_exceeded", extra={"path": request.path, "count": count, "budget": 50}, ) return response中间件
推荐中间件顺序
Django 中间件按列表顺序依次执行(请求阶段)和逆序执行(响应阶段)。顺序不对可能导致 CORS 失效或 CSRF 误判:
MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", # 1. 安全头:HSTS、XSS 等 "whitenoise.middleware.WhiteNoiseMiddleware", # 2. 静态文件(尽量靠前) "django.contrib.sessions.middleware.SessionMiddleware", # 3. 会话 "corsheaders.middleware.CorsMiddleware", # 4. CORS(必须在 Common 之前) "django.middleware.common.CommonMiddleware", # 5. URL 规范化 "django.middleware.csrf.CsrfViewMiddleware", # 6. CSRF 保护 "django.contrib.auth.middleware.AuthenticationMiddleware", # 7. 用户认证 "django.contrib.messages.middleware.MessageMiddleware", # 8. 消息 "django.middleware.clickjacking.XFrameOptionsMiddleware", # 9. 点击劫持防护]CorsMiddleware 必须在 CommonMiddleware 之前,否则跨域请求可能无法正确处理 OPTIONS 预检。
自定义中间件示例
请求 ID 中间件——为每个请求生成唯一标识,贯穿日志和响应头:
import uuid
class RequestIDMiddleware: def __init__(self, get_response): self.get_response = get_response
def __call__(self, request): request.id = request.headers.get("X-Request-ID", str(uuid.uuid4())) response = self.get_response(request) response["X-Request-ID"] = request.id return response注意:Django 6.0 支持异步中间件,但混用同步和异步中间件会引入
sync_to_async包装开销。如果项目以 WSGI 为主,全部使用同步中间件是更好的选择。
信号:谨慎使用
Django 信号(post_save、pre_delete 等)提供了一种松耦合的事件通知机制。但 2026 年社区共识是:业务逻辑优先用显式的 Service 层调用,而非隐式的信号。
信号适合的场景
- 第三方 app 集成点(无法修改源码)
- 缓存失效(模型变更后自动清除缓存)
- 搜索索引同步(模型保存后更新 Elasticsearch)
- 审计日志(记录谁在什么时候改了哪个字段)
信号不适合的场景
- 下单后发邮件 → 用 Service 层显式调用
- 用户注册后创建默认配置 → 用 Service 层
- 数据校验和处理 → 用 Model 的
clean()或 Form 校验
Service 层替代模式
对比两种写法:
# 信号方式:逻辑分散,隐式触发,难以追踪from django.db.models.signals import post_save
@receiver(post_save, sender=Order)def on_order_created(sender, instance, created, **kwargs): if created: send_confirmation_email(instance) update_inventory(instance) update_search_index(instance)# Service 层:逻辑集中,显式调用,易于测试和调试class OrderService: def place_order(self, user, items): order = Order.objects.create(user=user) for item in items: OrderItem.objects.create(order=order, product=item.product, quantity=item.qty) self._update_inventory(order) self._send_confirmation_email(order) self._update_search_index(order) return orderService 层的好处:调用链清晰、单步调试友好、单元测试不需要 mock 信号。
Django 6.0 异步编程
Django 6.0(2025 年 12 月发布)是异步支持的一个重要里程碑。各组件异步支持状态:
| 组件 | 状态 |
|---|---|
| 视图(函数 + 类) | 原生异步 |
| 中间件 | 原生异步 |
| 信号 | 并发调度(asyncio.gather) |
| 认证 | alogin()、alogout()、aauthenticate() |
| 会话 | 所有后端异步 |
| 缓存 | aget()、aset()、adelete() |
| 分页 | AsyncPaginator(6.0 新增) |
| 后台任务 | 内置 @task 装饰器(6.0 新增) |
| ORM | aget()/acreate() 等异步方法可用,底层仍是 sync_to_async() 包装 |
| 模板渲染 | 无异步标签和过滤器 |
| Forms | 完全同步 |
关键认知:不要为了异步而异步。据 JetBrains 2025 调查,仅约 14% 的 Django 开发者实际使用异步视图。异步真正有价值的场景:
- 并发外部 API 调用(
asyncio.gather模式) - WebSocket(Django Channels)
- 高并发下需要可预测延迟
- LLM/AI 功能(并发推理)
异步 ORM 使用
# 异步视图 + 异步 ORMfrom django.http import JsonResponsefrom .models import Post
async def post_detail(request, post_id: int): post = await Post.objects.filter(id=post_id).afirst() if not post: return JsonResponse({"error": "文章不存在"}, status=404) return JsonResponse({"title": post.title, "body": post.body})并发查询
异步 ORM 的真正优势在于并发执行多个独立查询:
import asyncio
async def dashboard(request): users, orders, revenue = await asyncio.gather( User.objects.acount(), Order.objects.filter(status="pending").acount(), Order.objects.filter(status="completed").aaggregate(total=Sum("amount")), ) # 三个查询并发执行,总耗时 ≈ 最慢的一个,而非三者之和 return JsonResponse({"users": users, "pending_orders": orders, "revenue": revenue})内置后台任务(Django 6.0 新增)
Django 6.0 引入了轻量级的 @task 装饰器,比 Celery 更轻,适合简单异步操作:
from django.tasks import task
@taskdef send_welcome_email(user_id: int): # 在后台执行,不阻塞请求 ...注意:@task 适合轻量级 fire-and-forget 场景。需要重试、调度、分布式执行的任务仍建议使用 Celery。
Django REST Framework 进阶
DRF 是 Django 构建 REST API 的事实标准。以下是一些进阶模式:
ViewSet + Router 组合
避免为同一资源重复写多个视图:
from rest_framework.viewsets import ModelViewSetfrom rest_framework.decorators import actionfrom rest_framework.response import Response
class PostViewSet(ModelViewSet): queryset = Post.objects.select_related("author").prefetch_related("tags") serializer_class = PostSerializer permission_classes = [IsAuthenticatedOrReadOnly]
@action(detail=True, methods=["post"]) def publish(self, request, pk=None): post = self.get_object() post.status = "published" post.save() return Response({"status": "published"})
@action(detail=False, methods=["get"]) def popular(self, request): posts = self.get_queryset().filter(views__gt=1000)[:20] return Response(self.get_serializer(posts, many=True).data)序列化器性能优化
避免在 Serializer 中触发 N+1。确保 queryset 已包含 select_related / prefetch_related:
class PostViewSet(ModelViewSet): # 这个 queryset 会被 ViewSet 的所有 action 复用 queryset = Post.objects.select_related("author").prefetch_related("tags")对于复杂嵌套序列化,用 SerializerMethodField 时要特别注意——它是 N+1 的高发区。考虑用 prefetch_related + Prefetch 对象预处理关联数据。
自定义异常处理
统一 API 错误响应格式:
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context): response = exception_handler(exc, context) if response is not None: response.data = { "error": { "code": getattr(exc, "default_code", "error"), "message": response.data.get("detail", str(exc)), } } return response在 settings.py 中配置:
REST_FRAMEWORK = { "EXCEPTION_HANDLER": "myapp.exceptions.custom_exception_handler", "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination", "PAGE_SIZE": 20, "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "rest_framework.throttling.UserRateThrottle", ], "DEFAULT_THROTTLE_RATES": {"anon": "100/hour", "user": "1000/hour"},}缓存策略
Django 提供多层次的缓存支持,推荐逐层启用:
视图缓存
对不常变化的页面(首页、列表页)做整页缓存:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 缓存 15 分钟def homepage(request): ...模板片段缓存
只缓存页面中代价最高的部分:
{% load cache %}{% cache 300 sidebar request.user.id %} <!-- 复杂的侧栏逻辑 -->{% endcache %}底层缓存 API
在 Service 层精确控制缓存:
from django.core.cache import cache
def get_popular_posts(): key = "popular_posts" posts = cache.get(key) if posts is None: posts = list(Post.objects.filter(views__gt=1000).order_by("-views")[:20]) cache.set(key, posts, 300) # 5 分钟 return posts推荐使用 Redis 作为缓存后端(通过 django-redis),生产环境下 LocMemCache 无法在多进程间共享。
后台管理定制
Django Admin 不只是开发工具,它可以作为内部运营后台直接上线:
ModelAdmin 常用定制
from django.contrib import admin
@admin.register(Post)class PostAdmin(admin.ModelAdmin): list_display = ["title", "author", "status", "pub_date", "views"] list_filter = ["status", "pub_date"] search_fields = ["title", "body"] readonly_fields = ["created_at", "updated_at", "views"] ordering = ["-pub_date"] date_hierarchy = "pub_date" actions = ["make_published", "make_draft"]
@admin.action(description="发布选中文章") def make_published(self, request, queryset): queryset.update(status="published")这种程度的定制已经能满足大多数内部管理需求,无需额外开发界面。
安全加固
生产环境必须配置的安全项:
# settings.py — production.pyDEBUG = FalseALLOWED_HOSTS = ["yourdomain.com"]SECURE_SSL_REDIRECT = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = TrueSECURE_HSTS_SECONDS = 31536000SECURE_HSTS_INCLUDE_SUBDOMAINS = TrueSECURE_HSTS_PRELOAD = TrueSECURE_BROWSER_XSS_FILTER = TrueSECURE_CONTENT_TYPE_NOSNIFF = TrueX_FRAME_OPTIONS = "DENY"CORS_ALLOW_ALL_ORIGINS = FalseCORS_ALLOWED_ORIGINS = ["https://your-frontend.com"]永远不要在生产环境使用 ALLOWED_HOSTS = ["*"] 或 CORS_ALLOW_ALL_ORIGINS = True。
部署架构
2026 年 Django 生产部署的典型架构:
CDN(Cloudflare / 阿里云 CDN) │ Nginx / Caddy(TLS 终结 + 静态文件) │ ┌───────────────┼───────────────┐ │ │ │ Gunicorn Uvicorn/Daphne Celery Worker (WSGI 主力) (ASGI,如用 Channels) │ │ │ │ └───────────────┼───────────────┘ │ PgBouncer(连接池) │ ┌──────────┴──────────┐ │ │ PostgreSQL Redis (主数据库) (缓存/队列/会话)关键决策:
- WSGI 还是 ASGI? 大部分项目用 Gunicorn(WSGI)即可。需要 WebSocket 或异步视图时才用 Uvicorn/Daphne。
- PgBouncer 必不可少:Gunicorn worker x Celery worker x 线程 → 连接数轻松破千,PgBouncer 将其收敛为 20-50 个实际连接。
- SETTINGS 分环境:
config/settings/{base,development,production,test}.py,用环境变量切换。
常用命令速查
python manage.py shell_plus # Shell Plus(django-extensions,自动导入模型)python manage.py showmigrations # 查看迁移状态python manage.py sqlmigrate app 0001 # 查看迁移生成的 SQLpython manage.py check --deploy # 生产安全检查python manage.py test --parallel # 并行运行测试python manage.py collectstatic # 收集静态文件python manage.py clearsessions # 清理过期会话python manage.py makemigrations --name fix_slug # 生成命名迁移常见问题
查询慢怎么办
- 先用
django-debug-toolbar查看页面触发了多少次 SQL - 打开 SQL 面板,找重复查询和慢查询
- 加
select_related/prefetch_related/only/defer - 检查是否缺少索引,用
EXPLAIN确认查询计划 - 对高频只读接口加缓存
数据库连接耗尽
报 too many connections 时,立即检查 PgBouncer 是否正常工作:
pg_isready -h localhost -p 6432 # 检查 PgBouncer 状态没有 PgBouncer 的话,每个 Django worker 会持有一个数据库连接,再加上 Celery worker、管理命令、shell 连接等,很容易超出 PostgreSQL 的 max_connections 限制。
迁移失败或锁表
生产环境迁移前务必:
- 先在 staging 环境测试
- 检查迁移 SQL(
sqlmigrate) - 大表加字段用「expand-then-contract」策略:先加可空字段 → 后台回填数据 → 再设置为必填
- 避免在业务高峰期执行迁移
信号中调用数据库导致迁移出错
信号注册的 receiver 在 migrate 时也会触发。如果 receiver 中查询的表还未创建,就会报错。解决办法是在 AppConfig.ready() 中注册信号前做一次模型检查。
测试太慢
减少测试时间的方法:
--parallel并行运行- 测试数据库用 SQLite(内存模式,如果没用 PostgreSQL 特有功能)
- 对不需要数据库的测试设置
databases = None - 关闭密码哈希器(
PASSWORD_HASHERS用MD5PasswordHasher)
总结
Django 进阶围绕这些核心模式展开:
- ORM 优化:消灭 N+1、字段级控制、数据库端运算、合理索引——这是 Django 性能的基石
- 信号慎用:审计日志和缓存失效适合信号,业务流程用 Service 层显式调用
- Django 6.0 异步:内置后台任务、AsyncPaginator、并发查询,但只在 I/O 密集型场景真正受益
- 中间件顺序:CORS 必须在 Common 之前,WhiteNoise 尽量靠前
- DRF 进阶:ViewSet + Router 减少代码,prefetch 消灭嵌套序列化的 N+1
- 安全加固:HSTS、SSL Redirect、Secure Cookie、限定的 ALLOWED_HOSTS 和 CORS
- 部署铁律:PgBouncer 必不可少,settings 分环境,迁移先在 staging 验证
更多参考资料:Django 官方文档、Django REST Framework、Django 6.0 Release Notes。