Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I make Django QueryDict preserve ordering?

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'
like image 441
wim Avatar asked May 14 '14 18:05

wim


People also ask

Is Django Queryset immutable?

The QuerySet is immutable - chaining methods to our queryset doesn't modify the original queryset - it creates a new one.

What is QueryDict Django?

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.


2 Answers

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'
like image 104
pcoronel Avatar answered Oct 11 '22 14:10

pcoronel


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.

like image 24
alecxe Avatar answered Oct 11 '22 15:10

alecxe