title: Django Rendering Radio Button Options with Extra Information from Model
body: <p>I have a Location model and an associated form that displays the "location" field as a bunch of radio buttons (using the form queryset to display the values).  There are a small number of locations, but they need to be dynamic.  I'd like to display the location description next to each radio box option so users have a bit more info on the locations.  </p>
  
  <p>Pretend this is a list of radio buttons, this is what I'd like it to look like:</p>
  
  <p>&lt;> East  - This location is east.  &lt;> West  - This is the west location!  &lt;> North - This is the north location
  </p>
  
  <p>I have a model similar to the following:</p>
  
  <pre><code>class Location(models.Models):
      location = models.CharField(max_length=50)
      description = models.TextField(blank=True, null=True)
  </code></pre>
  
  <p>And a form as such: </p>
  
  <pre><code>class LocationForm(forms.Form):
      location = ExtraModelChoiceField(
                 widget=RadioSelect(renderer=ExtraHorizRadioRenderer), 
                 queryset = models.Locations.objects.filter(active=True))
  </code></pre>
  
  <p>I can't seem to find a good way to render the form so I can display the description along with each select option.  I've done a lot of overriding, but am not having too much luck.  </p>
  
  <p>MY ATTEMPT TO SOLVE (BUT NO LUCK YET): </p>
  
  <p>From what I gather, normally if a queryset is provided on the form field, the Django form logic translates that into a choices tupal of tupals.  Each "subtupal" contains an id and label that is displayed when it is rendered.  I'm trying to add a third value to those "subtupals" which would be a description. </p>
  
  <p>I've defined a custom renderer to display my radio buttons horizontally and to pass in my custom choices. </p>
  
  <pre><code>class ExtraHorizRadioRenderer(forms.RadioSelect.renderer):
      def render(self):
          return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))
  
      def __iter__(self):
          for i, choice in enumerate(self.choices):
              yield ExtraRadioInput(self.name, self.value, 
                                    self.attrs.copy(), choice, i)
  
      def __getitem__(self, idx):
          choice = self.choices[idx] # Let the IndexError propogate
          return ExtraRadioInput(self.name, self.value, 
                                 self.attrs.copy(), choice, idx)
  </code></pre>
  
  <p>I've overridden the Django RadioInput class so I can add the description information that I need to display next to the Radio Buttons. </p>
  
  <pre><code>class ExtraRadioInput(forms.widgets.RadioInput):
  
      def __init__(self, name, value, attrs, choice, index):
          self.name, self.value = name, value
          self.attrs = attrs
          self.choice_value = force_unicode(choice[0])
          self.choice_label = force_unicode(choice[1])
          self.choice_description = force_unicode(choice[2])   # &lt;--- MY ADDITION; FAILS
          self.index = index
  
      def __unicode__(self):
          if 'id' in self.attrs:
              label_for = ' for="%s_%s"' % (self.attrs['id'], self.index)
          else:
              label_for = ''
          choice_label = conditional_escape(force_unicode(self.choice_label))
          return mark_safe(u'&lt;label%s&gt;%s %s&lt;/label&gt;' % (
               label_for, self.tag(), choice_label))
  
      def tag(self):
          if 'id' in self.attrs:
              self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
          final_attrs = dict(self.attrs, type='radio', name=self.name, 
                        value=self.choice_value)
          if self.is_checked():
              final_attrs['checked'] = 'checked'
          return mark_safe(
             u'&lt;input%s /&gt;&lt;span class="description"&gt;%s&lt;/span&gt;' % \
             (flatatt(final_attrs),self.choice_description ))  # &lt;--- MY ADDTIONS
  </code></pre>
  
  <p>I've also overridden the following two Django classes hoping to pass around my modified choices tupals. </p>
  
  <pre><code>class ExtraModelChoiceIterator(forms.models.ModelChoiceIterator  ):    
  
      def choice(self, obj): 
          if self.field.to_field_name:
              key = obj.serializable_value(self.field.to_field_name)
          else:
              key = obj.pk
  
          if obj.description:   # &lt;-- MY ADDITIONS
              description = obj.description
          else:
              description = ""
          return (key, self.field.label_from_instance(obj),description)
  
  
  class ExtraModelChoiceField(forms.models.ModelChoiceField):
  
      def _get_choices(self):
          if hasattr(self, '_choices'):
              return self._choices
          return ExtraModelChoiceIterator(self)  # &lt;-- Uses MY NEW ITERATOR
  </code></pre>
  
  <p>Using the approach above, I can't seem to be able to pass around my 3-value tupal.  I get a "tuple index out of range" failure (up where I mark FAILURE above) indicating that somehow my tupal does not have the extra value.  </p>
  
  <p>Does anyone see a flaw in my logic, or more generally have an approach to displaying a description next to a list of choices using a widget?</p>
  
  <p>Thanks for reading. Any comments are much appreciated.
  Joe</p>