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:
- assemble the choices into a list (any iterable structure will do)
- pass the structure to the form’s __init__ to create our radio options on the fly
- use a comprehension list to create an extended description for each radio option
- tickets.code – as in a ticket code
- label – a pithy short description
- help – a longer description
The data structure:
Tickets are my own classes and they have attributes:
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()
)