klenwell information services : AppengineFormWidgetOverriding

Google App Engine: Widget Overriding


By default when you define a model with choices and create a modelform, the widget for that model (the html output) will be a select box. But what if you want the user to be able to select more than one choice. Then you'd want something like a list of checkboxes. The django API offers such a widget. But getting it to function was a bit of a challenge.

In my case, I was trying to figure this out in the context of a wizard I was putting together. (I know djangoforms offer its own wizard framework, but the bundled Google version doesn't support it yet.) So my code a little more complicated, but I've simplified it below.

This is what worked:
# set choice list: notice that we require a tuple of tuples containing both value and label items
# the google examples gave me the impression you could use a 1-D sequence
  # value, label

# our class with the picktwo field using choice list above
class DemoModel(db.Model):
  email    = db.EmailProperty(required=True)
  url      = db.LinkProperty(required=True)
  picktwo  = db.StringProperty(required=True, choices=CHOICES_PICKTWO)
  message  = db.StringProperty(required=True)
  created  = db.DateTimeProperty(auto_now_add=True)
  modified = db.DateTimeProperty(auto_now=True)

# extending charField with own class that overrides clean method (used for validation) so that
# it requires two items be picked from list
class PickTwoField(forms.CharField):  
  def clean(self, value):
    if len(value) != 2:
      raise forms.ValidationError('Please select two (2) choices below')
    return value

# modelform class
class DemoModelForm(djangoforms.ModelForm):
    NOTE: we have to explicitly pass our choice list as a key argument to our widget constructor
    Some examples I saw gave me the impression that you could just set widget to a value
    like so: widget=forms.CheckboxSelectMultiple
  picktwo = PickTwoField(widget=forms.CheckboxSelectMultiple(choices=CHOICES_PICKTWO))
  class Meta:
    model = DemoModel
    fields = ('email', 'picktwo')
html_form = """
<form method="
POST" action="%s">
<input type="
submit" value="continue" />
"" % ( action, DemoModelForm() )