文章列表
9 分钟阅读

Django 进阶使用指南


系列:Python Web 框架

第 4 / 11 篇

  1. FastAPI 安装与使用指南
  2. FastAPI 进阶使用指南
  3. Django 安装与使用指南
  4. Django 进阶使用指南
  5. Django 与 FastAPI 对比:如何选择合适的 Python Web 框架
  6. Python Web 框架有哪些,怎么选
  7. Flask 安装与使用指南
  8. Django REST Framework 入门:把 Django 做成 API
  9. FastAPI 项目实战:用户登录与 JWT 鉴权
  10. Python Web 项目部署指南:Gunicorn、Uvicorn、Nginx 和 Docker
  11. Python Web 项目结构怎么设计

Django 安装与使用指南 覆盖了项目创建、ORM 基础、模板和后台管理等入门内容。本文继续深入,介绍 ORM 优化、Django 6.0 异步特性、中间件、信号、DRF 进阶、缓存策略和生产部署——这些是「把 Django 项目真正上线并扛住流量」必须掌握的内容。

本文基于 Django 6.0(2025 年 12 月发布)和 DRF >=3.14 的当前技术栈,部分示例可直接用于生产环境。

ORM 进阶与优化

Django ORM 是框架最强大的组件之一。用好它能避免大多数性能问题,用错则可能拖垮整个应用。

关联查询:消灭 N+1

最常见的性能陷阱是关联查询逐条执行:

queryset_examples.py
# 反模式:N+1 查询
for post in Post.objects.all():
print(post.author.name) # 每篇文章触发 1 次额外查询
# 推荐:用 select_related 做 JOIN
for post in Post.objects.select_related("author"):
print(post.author.name) # 无额外查询

使用规则很简单:

方法适用场景SQL 次数
select_related()ForeignKey / OneToOne1 次 JOIN
prefetch_related()ManyToMany / 反向 ForeignKey2 次查询,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, Window
from 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 Window
from django.db.models.functions import Rank
posts_with_rank = Post.objects.annotate(
rank=Window(
expression=Rank(),
order_by=F("views").desc(),
)
)

索引策略

不要对所有字段盲目加索引,只为高频查询条件建立:

models.py
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 用户还可以考虑部分索引和表达式索引来覆盖更精准的查询场景。

查询预算中间件

生产环境中,一个简单办法是限制每个视图的查询数,超过阈值告警:

middleware.py
from django.db import connection
import 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_savepre_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 order

Service 层的好处:调用链清晰、单步调试友好、单元测试不需要 mock 信号。

Django 6.0 异步编程

Django 6.0(2025 年 12 月发布)是异步支持的一个重要里程碑。各组件异步支持状态:

组件状态
视图(函数 + 类)原生异步
中间件原生异步
信号并发调度(asyncio.gather
认证alogin()alogout()aauthenticate()
会话所有后端异步
缓存aget()aset()adelete()
分页AsyncPaginator(6.0 新增)
后台任务内置 @task 装饰器(6.0 新增)
ORMaget()/acreate() 等异步方法可用,底层仍是 sync_to_async() 包装
模板渲染无异步标签和过滤器
Forms完全同步

关键认知:不要为了异步而异步。据 JetBrains 2025 调查,仅约 14% 的 Django 开发者实际使用异步视图。异步真正有价值的场景:

  1. 并发外部 API 调用(asyncio.gather 模式)
  2. WebSocket(Django Channels)
  3. 高并发下需要可预测延迟
  4. LLM/AI 功能(并发推理)

异步 ORM 使用

# 异步视图 + 异步 ORM
from django.http import JsonResponse
from .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
@task
def 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 ModelViewSet
from rest_framework.decorators import action
from 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.py
DEBUG = False
ALLOWED_HOSTS = ["yourdomain.com"]
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
CORS_ALLOW_ALL_ORIGINS = False
CORS_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,用环境变量切换。

常用命令速查

Terminal window
python manage.py shell_plus # Shell Plus(django-extensions,自动导入模型)
python manage.py showmigrations # 查看迁移状态
python manage.py sqlmigrate app 0001 # 查看迁移生成的 SQL
python manage.py check --deploy # 生产安全检查
python manage.py test --parallel # 并行运行测试
python manage.py collectstatic # 收集静态文件
python manage.py clearsessions # 清理过期会话
python manage.py makemigrations --name fix_slug # 生成命名迁移

常见问题

查询慢怎么办

  1. 先用 django-debug-toolbar 查看页面触发了多少次 SQL
  2. 打开 SQL 面板,找重复查询和慢查询
  3. select_related / prefetch_related / only / defer
  4. 检查是否缺少索引,用 EXPLAIN 确认查询计划
  5. 对高频只读接口加缓存

数据库连接耗尽

too many connections 时,立即检查 PgBouncer 是否正常工作:

Terminal window
pg_isready -h localhost -p 6432 # 检查 PgBouncer 状态

没有 PgBouncer 的话,每个 Django worker 会持有一个数据库连接,再加上 Celery worker、管理命令、shell 连接等,很容易超出 PostgreSQL 的 max_connections 限制。

迁移失败或锁表

生产环境迁移前务必:

  1. 先在 staging 环境测试
  2. 检查迁移 SQL(sqlmigrate
  3. 大表加字段用「expand-then-contract」策略:先加可空字段 → 后台回填数据 → 再设置为必填
  4. 避免在业务高峰期执行迁移

信号中调用数据库导致迁移出错

信号注册的 receiver 在 migrate 时也会触发。如果 receiver 中查询的表还未创建,就会报错。解决办法是在 AppConfig.ready() 中注册信号前做一次模型检查。

测试太慢

减少测试时间的方法:

  • --parallel 并行运行
  • 测试数据库用 SQLite(内存模式,如果没用 PostgreSQL 特有功能)
  • 对不需要数据库的测试设置 databases = None
  • 关闭密码哈希器(PASSWORD_HASHERSMD5PasswordHasher

总结

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 FrameworkDjango 6.0 Release Notes