There is only one truth. It is the source.
April 24, 2015
Tags: django
If you have a page that uses a form that submits GET requests (possibly a search form or similar), you sometimes want to be able to create links on that page that contain the same querystring as submitted. In other words you need a way to convert a bound or unbound form to a querystring. Django does not have a built-in API for doing this. In most cases you can get away with simply unpacking values from the form's cleaned_data or initial dictionary and converting that into a querystring. This has an interesting failure when done with a MultipleChoiceField:
In [1]: import urllib In [2]: from django.http import QueryDict In [3]: d = QueryDict(mutable=True) In [4]: d['foo'] = [] # The initial value of a MultipleChoiceField is [] In [5]: d.urlencode() Out[5]: u'foo=%5B%5D' In [6]: urllib.unquote(u'foo=%5B%5D') Out[6]: u'foo=[]'
Clearly this is not what we want. I solved this by creating a new method on form fields called convert_to_querystring_dict() and a helper function get_query_dict_from_form():
class QuerystringableMultipleChoiceField(forms.MultipleChoiceField): def convert_to_querystring_dict(self, field_name, value): if value == []: if not self.required: return {} else: return {field_name: ''} return {field_name: value} class QuerystringableBooleanField(forms.BooleanField): def convert_to_querystring_dict(self, field_name, value): if not self.to_python(value): if not self.required: return {} else: return {field_name: ''} else: return {field_name: 'on'} class QuerystringableChoiceField(forms.ChoiceField): def convert_to_querystring_dict(self, field_name, value): if self.to_python(value) == '': if not self.required: return {} else: return {field_name: ''} return {field_name: self.to_python(value)} def get_query_dict_from_form(form, require_querystringable_fields=False): query_dict = QueryDict('', mutable=True) for field in form.fields.keys(): the_field = form.fields[field] if hasattr(the_field, 'convert_to_querystring_dict'): if form.is_bound: querystring_dict = the_field.convert_to_querystring_dict( field, form.cleaned_data.get(field)) else: querystring_dict = the_field.convert_to_querystring_dict( field, form.initial.get(field)) for k, v in querystring_dict.items(): if isinstance(v, (list, tuple)): for x in v: query_dict.appendlist(k, x) else: query_dict[k] = v elif require_querystringable_fields and not hasattr( the_field, 'convert_to_querystring_dict'): raise ValueError( '{} does not have a convert_to_querystring_dict method'.format( the_field)) else: if form.is_bound: query_dict[field] = form.cleaned_data.get(field) else: query_dict[field] = form.initial.get(field) return query_dict