QueryDict
class is based on MultiValueDict
class that is based on regular python dict
, which is an unordered collection as you know.
According to the source code, QueryDict
internally uses urlparse.parse_qsl()
method, which preserves the order of query parameters, outputs a list of tuples:
>>> 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')]
What you can do, is to use the order of keys given by the parse_qsl()
for sorting:
>>> order = [key for key, _ in parse_qsl('x=foo³&y=bar(potato),z=hello world')]
>>> order
['x', 'y']
Then, subclass QueryDict
and override lists()
method used in 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'
The approach is a bit ugly and may need further improvement, but hope at least it'll give you an idea of what is happening and what you can do about it.