可以让Django QueryDict保持顺序吗?
有没有办法让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'
这种方法有点复杂,可能还需要进一步改进,但希望至少能让你明白发生了什么,以及你可以怎么做。