可以让Django QueryDict保持顺序吗?

9 投票
2 回答
1998 浏览
提问于 2025-04-18 06:34

有没有办法让Django的QueryDict保持原始查询字符串的顺序?

>>> from django.http import QueryDict
>>> q = QueryDict(u'x=foo³&y=bar(potato),z=hello world')
>>> q.urlencode(safe='()')
u'y=bar(potato)%2Cz%3Dhello%20world&x=foo%C2%B3'

2 个回答

3

QueryDict 是 Django 中的一个类,它是从 MultiValueDict 继承而来的,而 MultiValueDict 又是从 dict 继承的。简单来说,dict 是用一种叫做哈希表的方式来实现的,所以我们不能保证它的顺序是固定的。

我不确定这是否对你有帮助,但 有一点是 QueryDict 会保留的,那就是它里面的“列表”的顺序(也就是同一个键对应多个值的情况)。利用这一点,你可以这样做:

>>> from django.http import QueryDict
>>> q = QueryDict(u'x=foo³&x=bar(potato),x=hello world')
>>> q.lists()
[('x', ['foo³', 'bar(potato)', 'hello world'])]
>>> q.urlencode(safe='()')
u'x=foo%C2%B3&x=bar(potato)&x=hello%20world'
2

QueryDict 类是基于 MultiValueDict 类的,而 MultiValueDict 又是基于普通的 Python dict。你知道,dict 是一种无序的集合。

根据 源代码QueryDict 在内部使用 urlparse.parse_qsl() 方法,这个方法可以保留查询参数的顺序,并输出一个元组的列表:

>>> from urlparse import parse_qsl
>>> parse_qsl('x=foo³&y=bar(potato),z=hello world')
[('x', 'foo\xc2\xb3'), ('y', 'bar(potato),z=hello world')]

你可以利用 parse_qsl() 提供的键的顺序来进行排序:

>>> order = [key for key, _ in parse_qsl('x=foo³&y=bar(potato),z=hello world')]
>>> order
['x', 'y']

然后,创建一个 QueryDict 的子类,并重写 lists() 方法,这个方法在 urlencode() 中使用:

>>> class MyQueryDict(QueryDict):
...     def __init__(self, query_string, mutable=False, encoding=None, order=None):
...         super(MyQueryDict, self).__init__(query_string, mutable=False, encoding=None)
...         self.order = order
...     def lists(self):
...         return [(key, self.getlist(key)) for key in self.order]
... 
>>> q = MyQueryDict(u'x=foo³&y=bar(potato),z=hello world', order=order)
>>> q.urlencode(safe='()')
u'x=foo%C2%B3&y=bar(potato)%2Cz%3Dhello%20world'

这种方法有点复杂,可能还需要进一步改进,但希望至少能让你明白发生了什么,以及你可以怎么做。

撰写回答