Python
配置、中间件、信号与管理命令
发布于 2026年7月23日
配置、中间件、信号与管理命令
本篇介绍 Django 的常用扩展点:多环境配置、中间件、信号、自定义管理命令和应用启动配置。它们能让项目结构更清楚,但也容易被滥用。
一、按环境拆分配置
推荐结构:
mysite/
settings/
__init__.py
base.py
development.py
production.py
base.py 保存公共配置,development.py 和 production.py 只覆盖差异。
# settings/development.py
from .base import *
DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
启动时指定配置:
$env:DJANGO_SETTINGS_MODULE = "mysite.settings.development"
python manage.py runserver
manage.py、wsgi.py、asgi.py 也可设置默认配置模块。
二、读取环境变量
import os
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true"
ALLOWED_HOSTS = [
host.strip()
for host in os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")
if host.strip()
]
生产密钥、数据库密码、邮件密码和第三方令牌不能写入仓库。可以提交 .env.example 说明需要哪些变量,但不放真实值。
关键配置缺失时应快速失败,使用 os.environ["NAME"] 比悄悄使用不安全默认值更可靠。
三、中间件是什么
中间件包裹整个请求/响应流程,适合处理跨应用的通用逻辑,例如安全头、会话、认证、请求 ID 和耗时记录。
# core/middleware.py
import time
import uuid
class RequestContextMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
request.request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
started = time.monotonic()
response = self.get_response(request)
response["X-Request-ID"] = request.request_id
response["Server-Timing"] = f"app;dur={(time.monotonic() - started) * 1000:.2f}"
return response
在 MIDDLEWARE 中注册:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"core.middleware.RequestContextMiddleware",
]
请求阶段按从上到下执行,响应阶段按从下到上返回。依赖 request.user 的中间件必须放在认证中间件之后。
四、信号
信号允许某个事件发生后通知其他代码:
# notes/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Message, MessageAudit
@receiver(post_save, sender=Message)
def create_message_audit(sender, instance, created, **kwargs):
if created:
MessageAudit.objects.create(message=instance, action="created")
在应用配置中导入信号:
# notes/apps.py
from django.apps import AppConfig
class NotesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "notes"
def ready(self):
from . import signals # noqa: F401
信号适合弱耦合的附加行为,不适合隐藏核心业务流程。付款、扣库存等关键操作应由明确的服务函数调用,这样顺序和事务边界更清楚。
注意:bulk_create()、bulk_update() 和 QuerySet update() 不会触发常规模型保存信号。
五、自定义管理命令
目录:
notes/
management/
__init__.py
commands/
__init__.py
archive_messages.py
from datetime import timedelta
from django.core.management.base import BaseCommand
from django.utils import timezone
from notes.models import Message
class Command(BaseCommand):
help = "归档指定天数以前的留言"
def add_arguments(self, parser):
parser.add_argument("--days", type=int, default=90)
parser.add_argument("--dry-run", action="store_true")
def handle(self, *args, **options):
cutoff = timezone.now() - timedelta(days=options["days"])
queryset = Message.objects.filter(created_at__lt=cutoff)
if options["dry_run"]:
self.stdout.write(f"将归档 {queryset.count()} 条留言")
return
updated = queryset.update(status="archived")
self.stdout.write(self.style.SUCCESS(f"已归档 {updated} 条留言"))
运行:
python manage.py archive_messages --days 180 --dry-run
批处理命令应支持幂等、日志、失败重试和 --dry-run,并避免一次把海量对象全部加载进内存。
六、系统检查框架
可以为项目关键配置增加检查:
from django.conf import settings
from django.core.checks import Error, register
@register()
def check_support_email(app_configs, **kwargs):
if not getattr(settings, "SUPPORT_EMAIL", ""):
return [Error("缺少 SUPPORT_EMAIL 配置", id="core.E001")]
return []
执行 python manage.py check 时会一起检查。
七、常见问题
- 生产读取到开发配置:
DJANGO_SETTINGS_MODULE指向错误。 - 中间件访问不到
request.user:顺序早于认证中间件。 - 信号重复执行:模块被重复注册,或测试/开发加载逻辑不清晰。
- 批量更新后信号没触发:这是 ORM 批量操作的正常行为。
- 管理命令占用大量内存:一次性遍历大 QuerySet,应使用
iterator()或分批处理。
八、本篇检查清单
- 能拆分开发与生产配置并使用环境变量。
- 理解中间件顺序和请求/响应方向。
- 知道信号适合附加行为,不隐藏关键业务。
- 能编写带参数和试运行模式的管理命令。
- 能用系统检查提前发现关键配置缺失。