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:
- 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.
- 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:
- 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)
- 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=...)