bigywater.tech
Django · 20

QueryDict 与重复参数

?ids=1&ids=2&ids=3 进了 request.GET 变成什么?为什么 int(request.GET["ids"]) 只拿到 3?这篇把 QueryDict 的每个取值方式都在同一组数据上跑一遍,再看字段数量限制。

最后一个
q[key] / q.get(key) / q.dict() / q.items() 都只给最后一个值;q.getlist / q.lists() / dict(q) 给全部
不可变
请求里的 QueryDict 默认不可变,copy() 才能改
1000
DATA_UPLOAD_MAX_NUMBER_FIELDS 默认值,GET 与 POST 超过一个都抛 TooManyFieldsSent

重复参数怎么取

实测 · 本机执行输出
QueryDict           = <QueryDict: {'a': ['1', '2'], 'b': ['x'], 'tag': ['py', 'django', 'orm'], 'empty': ['']}>
q['a']              = '2'   ← 直接下标取到的是最后一个值
q.get('a')          = '2'
q.getlist('a')      = ['1', '2']
q.getlist('tag')    = ['py', 'django', 'orm']
q.get('missing')    = None  q.get('missing','dflt') = dflt  q.getlist('missing') = []
q.get('empty')      = ''
dict(q)             = {'a': ['1', '2'], 'b': ['x'], 'tag': ['py', 'django', 'orm'], 'empty': ['']}
q.dict()            = {'a': '2', 'b': 'x', 'tag': 'orm', 'empty': ''}   ← 每个 key 只留最后一个值
list(q.items())     = [('a', '2'), ('b', 'x'), ('tag', 'orm'), ('empty', '')]
list(q.lists())     = [('a', ['1', '2']), ('b', ['x']), ('tag', ['py', 'django', 'orm']), ('empty', [''])]
len(q) = 4 (key 的个数,不是参数个数)   q.keys() 顺序 = ['a', 'b', 'tag', 'empty']

QueryDict 是「一个 key 对应多个值」的字典:大部分接口(下标、get、items、dict() 方法)只返回最后一个,少数(getlist、lists、内置 dict(q))返回全部。

不可变与编码

实测 · 本机执行输出
q._mutable = False
q['a'] = '3' → AttributeError: This QueryDict instance is immutable
copy() 之后赋值/追加: <QueryDict: {'a': ['3', '4'], 'b': ['x', 'y']}>  getlist("a") = ['3', '4']
urlencode() = a=3&a=4&b=x&b=y
解码后: {'q': ['中文'], 'x': ['a b'], 'y': ['&']}  再 urlencode(): q=%E4%B8%AD%E6%96%87&x=a+b&y=%26
safe 参数保留 "/" : next=/a/b/%3Fc%3D1

request.GET / request.POST 是不可变的;构造新的查询串用 copy() + urlencode(),中文与保留字符自动编码。

在请求里

实测 · 本机执行输出
request.GET.get("ids")      = 3    getlist = ['1', '2', '3']
int(request.GET["ids"])      = 3 (只取到最后一个)
POST 表单里 ids=[1,2,3]:  getlist = ['1', '2', '3']  get = 3

多选框、多个同名参数,一定要用 getlist。JSON 请求体里没有「重复 key」这回事,那是解析后的数组。

字段数上限

实测 · 本机执行输出
DATA_UPLOAD_MAX_NUMBER_FIELDS 默认 = 1000
POST 表单 1000 个字段 → request.POST 里 1000 个
POST 表单 1001 个字段 → TooManyFieldsSent: The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.
GET 查询串 1000 个参数 → request.GET 里 1000 个
GET 查询串 1001 个参数 → TooManyFieldsSent: The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.

GET 查询串也受限制(实验里是在访问 len(request.GET) 时抛出)。1000 个刚好通过,1001 个抛 TooManyFieldsSent。

分步演示:QueryDict 的取值

QueryDict
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。请求由 RequestFactory 构造
    • 没有验证:超大单个参数值的限制、非 UTF-8 编码的查询串(DEFAULT_CHARSET 与 request.encoding)、QueryDict 在 multipart 下与文件字段并存时的行为、第一次访问 request.GET 时才抛 TooManyFieldsSent 这一说法只按代码路径推断(实验里是在访问 len() 时触发)
    京ICP备2026067502号-1京公网安备11010502063487号