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.