Is it possible to to make Django's QueryDict preserve the ordering from the original query string?
>>> 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'
The QuerySet is immutable - chaining methods to our queryset doesn't modify the original queryset - it creates a new one.
class QueryDict. In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict , a dictionary-like class customized to deal with multiple values for the same key. This is necessary because some HTML form elements, notably <select multiple> , pass multiple values for the same key.
QueryDict
inherits from Django's MultiValueDict
which inherits from dict
which is implemented as a hash table. Thus, you can't guarantee it will stay ordered.
I'm not sure if this will be relevant to what you need, but an ordering that QueryDict does preserve is the order of "lists" (multiple values for the same key) passed in to them. Using this, you could do:
>>> 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'
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With