Django custom form widget for dictionary and tuple key-value pairs

one comment    Posted on December 20th, 2010

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:

django custom form widget for key-value input

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


Gathering Leads using MTurk

   Posted on June 13th, 2010

I want to share a technique with you that I have been using to streamline my customer development process. It’s experimental, but so far it’s saving me a lot of time and producing good results.

At the core, my technique involves using MTurk to collect targeted leads for some business ideas that have been brewing in my head. By using MTurk I was able to gather contact information from a huge list of websites in a very little amount of time. Sure, I could’ve written a scraper to find emails on most of these sites, but after weighing the cost/benefit ratio, I figured it just wasn’t worth my time.

Continue reading…



Daily Signal (May 10, 2010)

   Posted on May 10th, 2010

  • Pandas – R’s dataframes in Python
    • Pandas is a python package for working with timeseries data. I’ve been looking a long time for an equivalent to R’s dataframe functionality within python and this is it. What makes these dataframe structures so special is their ability to quickly slice and dice a table of data.
  • node.js, jsdom and jQuery

    • JSdom allows you emulate the entire document object model of a webpage without running a browser. I just found out today that node.js now runs jsdom and jquery seamlessly. This is nothing short of a webscraping revolution.
  • CryptoJS

    • A cryptographic library for Javascript. Perfect use case: Interactive API documentation. Got an api service? Want developers to adopt quicker? Allow them to submit signed API calls within your webpages.
  • The MarketPlace Play

    • A detailed analysis of the marketplace business model.