Here’s a code snippet to ease your pains when asking for user input in the form of key-value pairs of data. I’ve been using this widget in more and more applications as Redis and other lightweight databases displace some of my traditional SQL storage solutions.
I had to create this widget because none of the builtin django widgets could generate arbitrarily long lists of key-value pairs for the user to modify at runtime. It’s especially useful for storing and displaying dictionary data. Here’s an example of what it looks like after being rendered:

To use this, set the JsonPairInputs as the default widget on any form field. Here’s a simple example below:
examplejsonfield = forms.CharField(label = "Example JSON Key Value Field", required = False, widget = JsonPairInputs(val_attrs={'size':35}, key_attrs={'class':'large'}))
You may also pre-populate the input boxes if you pass a json-encoded list of key-value pairs to the
“initial” argument of the above CharField’s form.
widgets.py
import simplejson from django.forms import Widget from django.utils.encoding import force_unicode from django.utils.safestring import mark_safe from django.forms.widgets import flatatt class JsonPairInputs(Widget): """A widget that displays JSON Key Value Pairs as a list of text input box pairs Usage (in forms.py) : examplejsonfield = forms.CharField(label = "Example JSON Key Value Field", required = False, widget = JsonPairInputs(val_attrs={'size':35}, key_attrs={'class':'large'})) """ def __init__(self, *args, **kwargs): """A widget that displays JSON Key Value Pairs as a list of text input box pairs kwargs: key_attrs -- html attributes applied to the 1st input box pairs val_attrs -- html attributes applied to the 2nd input box pairs """ self.key_attrs = {} self.val_attrs = {} if "key_attrs" in kwargs: self.key_attrs = kwargs.pop("key_attrs") if "val_attrs" in kwargs: self.val_attrs = kwargs.pop("val_attrs") Widget.__init__(self, *args, **kwargs) def render(self, name, value, attrs=None): """Renders this widget into an html string args: name (str) -- name of the field value (str) -- a json string of a two-tuple list automatically passed in by django attrs (dict) -- automatically passed in by django (unused in this function) """ if value is None or value.strip() is '': value = '{}' twotuple = simplejson.loads(force_unicode(value)) ret = '' if value and len(value) > 0: for k,v in twotuple: ctx = {'key':k, 'value':v, 'fieldname':name, 'key_attrs': flatatt(self.key_attrs), 'val_attrs': flatatt(self.val_attrs) } ret += '<input type="text" name="json_key[%(fieldname)s]" value="%(key)s" %(key_attrs)s> <input type="text" name="json_value[%(fieldname)s]" value="%(value)s" %(val_attrs)s><br />' % ctx return mark_safe(ret) def value_from_datadict(self, data, files, name): """ Returns the simplejson representation of the key-value pairs sent in the POST parameters args: data (dict) -- request.POST or request.GET parameters files (list) -- request.FILES name (str) -- the name of the field associated with this widget """ if data.has_key('json_key[%s]' % name) and data.has_key('json_value[%s]' % name): keys = data.getlist("json_key[%s]" % name) values = data.getlist("json_value[%s]" % name) twotuple = [] for key, value in zip(keys, values): if len(key) > 0: twotuple += [(key,value)] jsontext = simplejson.dumps(twotuple) return jsontext

One response so far
Thanks for posting!
This was fairly useful for me.
I didn’t want to use one of the many JSONField implementations floating around because they seem to lack for South and other niceties.
There is a typo on the line
for k,v in twotuple:
It should read:
for k,v in twotuple.items():