[django] Creating dynamic lists of radio buttons from arbitrary classes and with long descriptions

I was looking for a way to put a lot of text next to the radio buttons in a django form. I also needed the list of radio buttons to automatically populate and vary according to the situation.

After the documentation for django being so awesome everywhere else; there is currently bugger all for this corner of the world: the radio widget. Fat lot of use that is.

It seems like django isn’t set up to do much to help you at first; but at second glance (ie: after wandering down vastly more complicated paths for hours) the solution in front your nose does work; with a bit of sourcery of course.

The second element of the tuple is presented inside the “label” tag – so any inline elements are permissible; for example:

The desired result

Or something like it

    <ul>
      <li><label for="id_ticket_0">
          <input type="radio" id="id_ticket_0" value="PARTTIME" name="ticket"/>
          <em>Part Time</em> Valid on Friday Night and Saturday Only
      </label></li>
      <li><label for="id_ticket_1">
          <input type="radio" id="id_ticket_1" value="DAYTIME" name="ticket"/>
          <em>Casual</em> Valid on Saturday Only
      </label></li>
      <li><label for="id_ticket_2">
           <input type="radio" id="id_ticket_2" value="EARLYBIRD" name="ticket"/>
           <em>Early Bird</em> Valid on Friday, Saturday, and Sunday. $15 discount for booking before 1am January 3rd, 2011
       </label></li>
    </ul>

The simple example

The trick is to “mark_safe” the content of the description then stuff whatever you need into:

    from django.utils.safestring import mark_safe
    choices = (
      ('1', mark_safe(u'<em>One</em> | This is the first option. It is awesome')),
      ('2', mark_safe(u'<em>Two</em> | This is the second option. Good too.'))
    )

The complex example

So in this example we:

  1. assemble the choices into a list (any iterable structure will do)
  2. pass the structure to the form’s __init__ to create our radio options on the fly
  3. use a comprehension list to create an extended description for each radio option
  4. The data structure:
    Tickets are my own classes and they have attributes:

    • tickets.code – as in a ticket code
    • label – a pithy short description
    • help – a longer description

    But more about that later. First lets create some instances:

        from mymodule import ticket
        # so lets create a few
        fulltime = ticket('FULLTIME',160,'Full Time',
                      "Valid Monday to Friday inclusive")
        parttime = ticket('PARTTIME',110,'Full Time',
                      "Valid outside of business hours only")
        daytime  = ticket('DAYTIME',70,'Day Time',
                      "Valid only on weekends and public holidays")
    
        # and put them together in a list any way you like
        available_tickets = [fulltime, parttime, daytime]
    
        # now create the form
        OrderForm(tickets=available_tickets)
    

    That probably happened in your *view* code. Now to see what happens in the *form*

        class OrderForm(ModelForm):
    
            def __init__(self, *args, **kwargs):
                self.tickets = kwargs.pop('tickets')
                super(OrderForm, self).__init__(*args, **kwargs)
    
                choices = [(t.code, mark_safe(u'<em>%s</em> %s' % (t.label, t.help)))
                        for t in self.tickets]
                self.fields['ticket'] = forms.ChoiceField(
                    choices = choices,
                    widget  = forms.RadioSelect()
                )
    
This entry was posted in geek and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>