Django QueryDict对象

前述章节我们使用到了 request.GET 与 request.Post,在 HttpRequest 对象中,GET 与 POST 属性都是一个 QueryDict 的实例,而在 Django 中,QueryDict 被定义在 django.http.request 中可以使用如下方式引入。

from django.http.request import QueryDict

request.POST 和 request.GET 的 QueryDict 在一个正常的请求/响应循环中是不可变的。若要使其改变,需要使用 copy() 方法。

我们说过 QueryDict 是一个类字典,所以它实现了 Python 字典数据类型的所有标准方法。在 Python 的字典中,如果一个键对应多个值,那么,对应键只会保留最后一个值,但是若在 HTML 表单中,一个键对应多个值是很正常的事情,比如下拉复选框 <select> 就是这种情况。而 QueryDict 的存在就是为了解决这个问题的,它允许一个键对应多个值,且它在一次请求到响应的过程中是不可变的。

1. 初识QueryDict 

QueryDict 继承自 MultiValueDict,而MultiValueDict 又继承自字典(dict),所以说它们都是字典的子类。接下来就让我们看一看 MultiValueDict 中定义的重要方法。

MultiValueDict 定义于 django/utils/datastructures.py 文件中,它也是 dict 的子类,用来处理多个值对应相同键的问题。同时,Django 在这个文件中还定义了一些其他的数据结构以适用于其他特定的场景。我们可以使用如下方式进行引入:

from django.utils.datastructures import MultiValueDict

在 MultiValueDict 类的注释中已经给出了常用的使用过程,如下所示:
>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
>>> d['name']
'Simon'
>>> d.getlist('name')
['Adrian', 'Simon']
>>> d.getlist('doesnotexist')
[]
>>> d.getlist('doesnotexist', ['Adrian', 'Simon'])
['Adrian', 'Simon']
>>> d.get('lastname', 'nonexistent')
'nonexistent'
>>> d.setlist('lastname', ['Holovaty', 'Willison'])
对于一个字典来说,最重要的当然是根据键获取值,而 MultiVauleDict 提供了三个重要方法,分别是:__getitem__、get、getlist。下面我们对上述三个方法做简单的介绍。

1) __getitem __方法

__getitem__是 Python 中的魔术方法,在 Python 中具有双下画线的方法都称作魔术方法。当类对象中定义了这个方法时,类实例就可以通过[]运算符取值。在这里MultiValueDict 是通过重写其父类 dict 来实现的,源码如下:
def __getitem__(self, key):
    """
返回此键的最后一个数据值,如果是空列表,则返回[];
如果没有找到,则引发键错误。
    """
    try:
        list_ = super().__getitem__(key)
    except KeyError:
        raise MultiValueDictKeyError(key)
    try:
        return list_[-1] #获取key对应值的最后一个
    except IndexError:
        return []
若果 key 不存在时,会抛出异常即 MultiValueDictKeyError 异常;若当一个 key 有多个值时,获取最后一个值。我们在视图中使用 request.GET['a'] 来获取 a 的值,就是使用了__getitem__方法。

2) get方法

我们也可以使用 request.GET.get('d',0) 获取 d 的值,但是它使用的是 MultValueDict 类中定义的 get 方法,其源码如下所示:
def get(self, key, default=None):
    """
返回传递的键的最后一个数据值。如果键不存在
或值为空列表,返回' default '。
    """
    try:
        val = self[key]
    except KeyError:
        return default
    if val == []:
        return default
    return val
分析源码可以得知,它首先尝试使用 val=self[key]获取 key 的值,如果获取不到则返回给定的默认值。但是你要注意这里捕获的异常是 KeyError,这个异常是 Python的标准异常类,而 MultiVauleDictKeyError 是它的一个子类。但是 get 和 __getitem__ 只能获取 key 的最后一个值,如果需要获取 key 的所有值就需要使用 getlist 方法。

3) getlist方法

这个方法同样也可以接受一个默认值,其内部实现了__getlist 方法,其源码如下所示:
def _getlist(self, key, default=None, force_list=False):
    """
返回键值的列表。用于在内部操作值列表。
如果force_list为真,返回值的新副本。
    """
    try:
        values = super().__getitem__(key)
    except KeyError:
        if default is None:
            return []
        return default
    else:
        if force_list:
            values = list(values) if values is not None else None
        return values
首选对源码进行初步分析,_getlist调用父类的__getitem__方法获取 key 对应的 values,如果不存在 key,则考虑使用 default。最后,force_list 的作用是将 values 复制一份,并保存在 values 变量中。

2. QueryDict 常用方法

前面介绍了 QueryDict 继承自 MultiValueDict,那么它有实现了方法呢?首先看一下它的构造函数:

QueryDict.__init__(query_string=None, mutable=False, encoding=None)

其中 mutable=False 表示对象不可变,反之则可变。如果把 URL 中的查询字符串直接传递给 QueryDict,会得到如下结果:
In [1]: from django.http.request import QueryDict
In [2]: QueryDict("a=1&b=2&c=3")
Out[2]: <QueryDict: {'a': ['1'], 'b': ['2'], 'c': ['3']}>
这就是通过 QueryDict 构造函数实现,如果感兴趣的小伙伴可以研究一下它的源码,就可以知道它的代码逻辑了,在这里不做分析。下面我们介绍一下它的经常用到的方法。

1) QueryDict.fromkeys()

这个方法的作用是循环可迭代对象中的每个元素作为键值,并赋予同样的值,它的方法格式如下所示:

QueryDict.fromkeys(iterable, value='', mutable=False, encoding=None)

实例如下:
In [4]: QueryDict.fromkeys(['a','b','c'],value="1")
Out[4]: <QueryDict: {'a': ['1'], 'b': ['1'], 'c': ['1']}>

2) QueryDict.pop(key)

返回给定键的值的列表,并从QueryDict中移除该键。 如果键不存在,将引发KeyError。实例如下:
In [11]: QueryDict('a=1&b=2&c=3',mutable=True).pop("a")
Out[11]: ['1']

3) QueryDict.popitem()

删除 QueryDict 任意一个键,并返回二值元组,包含键和键的所有值的列表。默认移除最后一个键值对。假如是一个空的字典上调用时将引发 KeyError。实例如下:
In [12]: q = QueryDict('a=1&a=2&c=3', mutable=True)
In [13]: q.popitem()
Out[13]: ('c', ['3'])

4) QueryDict.items()

类似 dict.items(),如果有重复项目,返回最近的一个,而不是都返回,它的返回值是一个列表包含的二元组。如下所示:
In [26]: q = QueryDict('a=1&a=2&a=3')
In [27]: q.items()
Out[27]: <generator object MultiValueDict.items at 0x01594AF0>
In [28]: list(q.items())
Out[28]: [('a', '3')]

5) QueryDict.values()

类似 dict.values(),它的返回值是一个生成器对象,而且只返回最近的值,实例如下:
In [23]: q = QueryDict('a=1&a=2&a=3')
In [24]: q.values()
Out[24]: <generator object MultiValueDict.values at 0x09D578F0>
In [25]: list(q.values())
Out[25]: ['3']

6)QueryDict.urlencode(safe=None)

已url的编码格式返回数据字符串。如下所示:
In [29]: q = QueryDict('a=2&b=3&b=5')
In [30]: q.urlencode()
Out[30]: 'a=2&b=3&b=5'

提示:使用 safe 参数传递不需要编码的字符,如 q.urlencode(safe='?')。

7) QueryDict.update(dict)

用新的 QueryDict 或字典更新当前 QueryDict。类似 dict.update(),但是追加内容,而不是更新并替换它们。如下示例:
In [31]: q = QueryDict('a=1', mutable=True)
In [32]: q.update({'a': '2'})
In [33]:  q.getlist('a')
Out[33]: ['1', '2']
本节对 QueryDict 做了详细的讲解,分别从继承关系上进行了分析,大家看完本节,应该能够熟练掌握它的使用方法,而且对于类字典对象也不再感到陌生。当然还有一部分方法没有介绍,有兴趣的伙伴可以参阅官方文档《QueryDict Object》进行学习。

推荐文章
系统是什么意思?

系统是什么意思?系统一词来源于英文system的音译,即若干部分相互联系、相互作用,形成的具有某些功能的整体。中国著名学者钱学森认为:系统是由相互作用相互依赖的若干组成部分结合而成的,具有特定功能的有

Java使用Lambda表达式遍历Iterator迭代器

Java8为Iterator引入了一个forEachRemaining(Consumeraction)默认方法,该方法所需的Consumer参数同样也是函数式接口。当程序调用Iterator的forE

IE:Mozilla/5.0(WindowsNT10.0;WOW64;Trident/7.0;Touch;rv:11.0)likeGecko Safari:Mozilla/5.0(Macinto

Linux重定向(输入输出重定向)详解

我们知道,Linux中标准的输入设备默认指的是键盘,标准的输出设备默认指的是显示器。而本节所要介绍的输入、输出重定向,完全可以从字面意思去理解,也就是: 输入重定向:指的是重新指定设备来代替键盘作

JSP Request.getHeader()方法:获得Http协议定义的文件头

该方法用于获得Http协议定义的文件头信息。 语法: getHeader(Stringname) 参数说明: name:header的名称。 返回值:header的取值。 示例 获取Ht

鼠标十字怎么恢复成箭头?

鼠标十字恢复成箭头的方法同时按下“win+R”,启动运行管理控制台。在运行命令框里输入“control”,如附图所示,即可打开控制面板窗口。点击“硬件和声音”。其中的设备包括电脑外设鼠标。点击"鼠标"

预处理命令和预处理器(CPP),C语言预处理命令和预处理器概述

C语言编程过程中,经常会用到如#include、#define等指令,这些标识开头的指令被称为预处理指令,预处理指令由预处理程序(预处理器)操作。较之其他编程语言,C/C++语言更依赖预处理器,故在阅

Servlet getServletConfig() 示例

Java选择排序法

假设当前存在一个int类型的数组number,该数组中的元素依次是13、15、24、99、4和1。如果使用冒泡排序进行两两相邻比较,第一趟排序后的结果如下: 13、15、24、4、1、99 第二

Linux日志分析工具(logwatch)安装及使用

日志是非常重要的系统文件,管理员每天的重要工作就是分析和查看服务器的日志,判断服务器的健康状态。但是日志管理又是一项非常枯燥的工作,如果需要管理员手工查看服务器上所有的日志,那实在是一项非常痛苦的工作

C语言switch case语句详解

C语言虽然没有限制ifelse能够处理的分支数量,但当分支过多时,用ifelse处理会不太方便,而且容易出现ifelse配对出错的情况。例如,输入一个整数,输出该整数对应的星期几的英文表示: #i

Java Swing星座选择器界面的实现

在了解各种基本组件的使用,以及常见事件的处理之后,本案例将综合文本框、按钮和下拉列表组件,实现一个星座选择器程序。程序允许用户在下拉列表中选择一个自己的星座,如果不在列表中还可以增加星座,也可以删除星

MySQL删除数据表(DORP TABLE语句)

在MySQL数据库中,对于不再需要的数据表,我们可以将其从数据库中删除。 在删除表的同时,表的结构和表中所有的数据都会被删除,因此在删除数据表之前最好先备份,以免造成无法挽回的损失。 下面我们来了

Go语言函数声明(函数定义)

函数构成了代码执行的逻辑结构,在Go语言中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句,每一个程序都包含很多的函数,函数是基本的代码块。 因为Go语言是编译型语言,

Spring Cloud Hystrix(熔断器)介绍及使用

Hystrix是Netflix针对微服务分布式系统采用的熔断保护中间件,相当于电路中的保险丝。 在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,

C++ search(STL search)算法详解

stringtext{"Smith,whereJoneshadhad\"had\",hadhad\"hadhad\".""\"Hadhad\"hadhadtheexaminers\'approva

pr补帧插件是什么?

pr补帧插件是:twixtor插件。twixtor插件是一款超级慢动作视频变速补帧插件,它能够”无极”的减慢、加速或变更连续图像的帧速,来带给你惊人的视觉效果。【插件语言】:英文【插件版本】:v7.3

广义表的存储结构详解(包含2种存储方案)

由于广义表中既可存储原子(不可再分的数据元素),也可以存储子表,因此很难使用顺序存储结构表示,通常情况下广义表结构采用链表实现。 使用顺序表实现广义表结构,不仅需要操作n维数组(例如{1,{2,{3

开源等于免费吗?用事实来说话

首先,开源软件和免费软件是两个概念: 开源软件是指公开源代码的软件。开源软件在发行的时候会附上软件的源代码,并授权允许用户更改、传播或者二次开发。 免费软件就是免费提供给用户使用的软件,但是在免

mysql8和5.7的区别是什么?

1.NoSql存储Mysql从5.7版本提供了NoSQL的存储功能,在8.0中这部分得到一些修改,不过这个在实际中用的极少2.隐藏索引隐藏索引的特性对于性能调试非常有用,在8.0中,索引可以被隐藏和显