Django + Vue 学习日志(04HttpRequest)
PART04:HttpRequest 对象、QueryDict 本质与 META 底层
记录时间:2026年8月6日
环境:Fedora 44 + Python 3.12.13 + Django 5.2.16
今日前言
今天的学习从"视图里那个 request 参数"开始,一路挖到 WSGI 的 environ 字典。
核心目标只有一个:搞清楚 request 到底是什么、从哪来、里面装了什么。
一、request.POST 是什么数据类型?
问题起源
在视图里写 request.POST.get('username') 时,突然意识到:
"POST 到底是个 dict 吗?为什么它能用
get()?"
正确答案
request.POST 不是 dict,而是 QueryDict。
from django.http import QueryDict
print(type(request.POST)) # <class 'django.http.request.QueryDict'>
QueryDict 的三大特性
| 特性 | 说明 |
|---|---|
| 一键多值 | 同一个 key 可以有多个 value |
| 值永远是字符串 | 不会自动转 int / float |
| 默认不可变 | 修改前需 .copy() |
为什么不用普通 dict?
因为 HTTP 表单天然支持"同名多值":
<input type="checkbox" name="hobby" value="篮球">
<input type="checkbox" name="hobby" value="足球">
<input type="checkbox" name="hobby" value="羽毛球">
如果用 dict,后面的会覆盖前面的,只剩一个值。
二、get() 和 getlist() 的区别
问题起源
"
get()和getlist()都是谁的方法?是 dict 的吗?"
正确答案
两者都是 QueryDict 的方法(继承自 MultiValueDict),不是 dict 的方法。
# QueryDict 的方法链
request.POST.get()
↓
QueryDict.get()
↓
MultiValueDict.get()
对比
| 方法 | 返回值 | 适用场景 |
|---|---|---|
get(key) |
单个字符串(最后一个值) | 普通单值字段 |
getlist(key) |
字符串列表 | 多选框、多文件上传 |
代码验证
# POST 数据:hobby=篮球&hobby=足球&hobby=羽毛球
request.POST.get('hobby') # '羽毛球'(最后一个)
request.POST.getlist('hobby') # ['篮球', '足球', '羽毛球']
一句话记忆
get()给你"一个答案",getlist()给你"全部答案"。
三、META 的本质
问题起源
"
request.META是什么?为什么request.method不用从 META 里取?"
正确答案
META 是一个字典,里面装着所有 HTTP 请求头和环境变量。
它来自 WSGI 服务器的 environ 字典。
# WSGI 服务器创建
request = HttpRequest(environ)
# environ 里的东西
environ['REQUEST_METHOD'] → request.method
environ['PATH_INFO'] → request.path_info
environ['HTTP_USER_AGENT'] → request.META['HTTP_USER_AGENT']
environ['REMOTE_ADDR'] → request.META['REMOTE_ADDR']
META 里有什么?
| Key | 含义 |
|---|---|
HTTP_USER_AGENT |
浏览器标识 |
HTTP_HOST |
请求域名 |
REMOTE_ADDR |
客户端 IP |
CONTENT_TYPE |
请求体格式 |
REQUEST_METHOD |
HTTP 方法(GET/POST) |
为什么 request.method 不用从 META 取?
因为 Django 已经帮你封装好了:
# Django 内部(简化)
class HttpRequest:
@property
def method(self):
return self.META.get('REQUEST_METHOD', 'GET')
✅ request.method 是对 META['REQUEST_METHOD'] 的属性封装
✅ 用起来更方便,不需要每次都写 META.get()
四、request 的其他常用属性
快速速查表
| 属性 | 类型 | 来源 |
|---|---|---|
request.method |
str | META 封装 |
request.path |
str | META 封装 |
request.GET |
QueryDict | URL 查询参数 |
request.POST |
QueryDict | 请求体(表单) |
request.body |
bytes | 原始请求体 |
request.headers |
dict-like | META 封装(Django 2.2+) |
request.META |
dict | WSGI environ |
request.FILES |
MultiValueDict | 上传文件 |
request.user |
User / AnonymousUser | Auth 中间件 |
五、HTML Form → Django 的完整链路
问题起源
"从我在浏览器填表单,到
request.POST.get()拿到值,中间到底发生了什么?"
完整链路(7 步)
① 浏览器渲染 HTML Form
<form method="POST" action="/login/">
<input name="username">
<input name="password">
<button>提交</button>
</form>
② 用户点击提交
→ 浏览器组装 HTTP 请求体
→ username=admin&password=123456
③ WSGI 服务器接收
→ 解析 HTTP 协议
→ 创建 environ 字典
→ 调用 Django 的 application(environ, start_response)
④ Django 核心处理
→ 创建 HttpRequest 对象
→ request.META = environ
→ request.POST = QueryDict(environ['wsgi.input'])
⑤ URLConf 匹配
→ path('login/', views.login)
→ 调用 login(request)
⑥ 视图函数执行
→ request.POST.get('username')
→ request.POST.get('password')
⑦ 返回 HttpResponse
→ Django 交给 WSGI
→ WSGI 发给浏览器
一句话串联
浏览器 → HTTP Body → WSGI → environ → QueryDict → request.POST → request.POST.get()
六、今日代码验证
视图函数
from django.http import HttpResponse
def login(request):
# 调试信息
print("META keys:", list(request.META.keys())[:10])
print("Method:", request.method)
print("Path:", request.path)
print("UA:", request.headers['User-Agent'])
if request.method == 'POST':
# QueryDict 的两种取法
username = request.POST.get('username', '')
password = request.POST.get('password', '')
# 多选框示例
hobbies = request.POST.getlist('hobby')
return HttpResponse(f'{username} likes {hobbies}')
return HttpResponse('GET request')
HTML 模板
<form method="POST">
{% csrf_token %}
<input name="username" placeholder="用户名">
<input name="password" type="password" placeholder="密码">
<input type="checkbox" name="hobby" value="篮球"> 篮球
<input type="checkbox" name="hobby" value="足球"> 足球
<input type="checkbox" name="hobby" value="羽毛球"> 羽毛球
<button type="submit">登录</button>
</form>
七、今日常见误区
❌ 误区 1:request.POST 能拿到 URL 参数
# URL: /search/?q=django
request.POST.get('q') # ❌ None
request.GET.get('q') # ✅ 'django'
✅ POST 只从请求体取,GET 参数在 URL 里。
❌ 误区 2:request.POST 永远有值
# 如果请求是 GET,POST 是空的 QueryDict
request.POST.get('username') # None(不是报错)
✅ 永远先做 if request.method == 'POST': 判断。
❌ 误区 3:修改 POST 数据直接赋值
request.POST['username'] = 'hacked' # ❌ 不可变
✅ 正确做法:
post_data = request.POST.copy()
post_data['username'] = 'hacked' # ✅
八、今日金句
"request 不是参数,而是 HTTP 请求在 Django 中的完整对象化表达。
META 是原油,QueryDict 是提炼后的汽油,request.POST.get() 是踩下油门的那一下。"
九、今日学习总结
| 知识点 | 掌握程度 |
|---|---|
request.POST 是 QueryDict 不是 dict |
✅ |
get() vs getlist() 的区别 |
✅ |
| META 来自 WSGI 的 environ | ✅ |
request.method 是对 META 的封装 |
✅ |
| HTML Form → Django 完整 7 步链路 | ✅ |
QueryDict 不可变,需 .copy() |
✅ |

浙公网安备 33010602011771号