Google App Engine, Django and MultipleChoiceField

I’m currently working on a Google App Engine / Django project and I got bitten by a nasty, poorly documented bug/issue. I’m trying to combine a StringListProperty with a MultipleChoiceField, so a user van basically set 0 or more values. Sounds simple. This is roughly what it looks like:

class MyModel(db.Model):
    field = db.StringListProperty(choices=["a","b","c"])

class MyForm(ModelForm):
    class Meta:
        model = MyModel

    field = forms.MultipleChoiceField(choices=[("a", "a"), ("b", "b"), ("c", "c")])

However, there are two issues with this:

  1. Upon submission of the form, if any of the options is selected, the data gets passed as a UnicodeMultiDict (something provided by webob), not a MultiValueDict which Django is used to. This means that the field validation can’t fetch the values as a list (it does not matter if a single or multiple values have been selected), it always gets a single value. And this won’t validate.
  2. StringListProperty explicitly converts its value to a string by joining on newlines in a Django form context, in stead of returning values as a list as you’d expect. This means that it can’t display a previously made selection, since “a\nb” won’t match values “a” or “b”.

These two issues can be solved as follows:

  1. Implement a Widgetclass that knows how to fetch its data from the MultiValueDict:
    class SelectMultiple(forms.widgets.SelectMultiple):
        def value_from_datadict(self, data, files, name):
            try:
                return data.getall(name)
            except:
                return data.get(name, None)
    
    and pass this to the Fields constructor: MultipleChoiceField(widget=SelectMultiple)
  2. Don’t use a StringListProperty with its odd, explicit side-effect. Use a ListProperty with a item_type unicode, e.g.
    field = db.ListProperty(item_type=unicode, choices=...)
    
Last updated April 18, 2013, 4:35 p.m.
comments powered by Disqus