你在写 Djanref="/tag/2029/" style="color:#E3A3CF;font-weight:bold;">go 视图函数时,是不是经常下意识地写 return HttpResponse('Hello'),却没想过这个响应到底经历了什么?请求进来,路由匹配,中间件流转,视图执行,模板渲染……这一整条链路,其实全藏在 Django 的几万行源码里。
入口在哪?从 wsgi.py 开始
打开项目根目录下的 wsgi.py,你会看到这行:
application = get_wsgi_application()它返回的是一个可调用对象(WSGI application),本质就是 django.core.handlers.wsgi.WSGIHandler 实例。这个类继承自 BaseHandler,而 BaseHandler 才是整个请求分发逻辑的真正心脏。
BaseHandler:请求的调度中心
翻到 django/core/handlers/base.py,找到 BaseHandler.get_response() 方法。它不处理具体业务,只干三件事:加载中间件、执行中间件链、把请求交给匹配的视图。
关键代码片段长这样:
response = self._middleware_chain(request)注意,_middleware_chain 不是手写的函数链,而是通过 MiddlewareMixin 和装饰器式包装动态生成的 callable——你每加一个中间件,它就自动串进这条链里。
中间件怎么“夹”住请求?
比如你写了这样一个简单中间件:
class LogIPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print(f'IP: {request.META.get("REMOTE_ADDR")}')
response = self.get_response(request)
return responseDjango 启动时会把它和所有其他中间件一起,按顺序组装成一个嵌套调用结构:LogIPMiddleware → AuthMiddleware → CsrfViewMiddleware → … → view()。
每个 __call__ 里的 self.get_response(request),实际指向下一个中间件(或最终视图)。
URL 匹配不是正则硬刚,而是 ResolverMatch 对象
别再以为 urls.py 就是靠 re.match() 一行行试的。Django 用的是 RegexURLResolver,它在启动时就把所有 URL 模式编译成正则对象并缓存。匹配成功后,返回一个 ResolverMatch 实例,里面不仅有 func(视图函数),还有 args、kwargs、url_name 等完整上下文。
你可以随时在视图里打印它:
def my_view(request):
print(request.resolver_match.func) # <function my_view at 0x...>
print(request.resolver_match.kwargs) # {'pk': '123'}这些信息,全是 BaseHandler 在调用 resolve() 后塞进 request 对象的。
为什么 render() 能自动找模板?
render(request, 'index.html') 看似简单,背后是 loader.get_template() 在干活。它会遍历 settings.TEMPLATES 里配置的所有后端(默认是 DjangoTemplates),再按 DIRS 和 APP_DIRS 顺序扫描路径,找到第一个存在的 index.html,然后编译成 Template 对象。模板里的 {{ user.name }},在渲染时才通过 Python 的 getattr 动态取值——所以模板出错,报的往往是 AttributeError,而不是语法错误。
框架不是黑盒,它只是把重复逻辑封装好了。你改一个 MIDDLEWARE 顺序,就是在调整函数调用栈;你加一个 @login_required,就是在视图外头又套了一层中间件逻辑。源码读多了,你会觉得写配置比写业务还上头。