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()

posted @ 2026-08-07 14:43  YiYezc  阅读(3)  评论(0)    收藏  举报