I did a little digging, and found a lead in the answer to this similar post that points to a somewhat outdated post in django-users.
Borrowing some code from the as_widget() method in forms.py I fashioned a method that I could add to my form in order to retrieve the RadioSelect widget's choices rendered as HTML.
class MyForm(forms.Form):
MY_CHOICES = (
('opt0', 'Option zero'),
('opt1', 'Option one'),
)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=MY_CHOICES)
def myfield_choices(self):
"""
Returns myfield's widget's default renderer, which can be used to
render the choices of a RadioSelect widget.
"""
field = self['myfield']
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.get_renderer(name, field.value(), attrs=attrs)
Then in the template you can access individual radio button choices like so:
<ul>
<li>
{{ myform.myfield_choices.0 }}
My custom HTML that goes with choice 0
</li>
<li>
{{ myform.myfield_choices.1 }}
Different HTML that goes with choice 1
</li>
</ul>
or
{% for choice in myform.myfield_choices %}
<div>
{{ choice }}
</div>
{% endfor %}
I'm pretty sure this is a bad idea. It will likely stop working at some point as django evolves. It violates DRY by copying code from as_widget(). (TBH, I didn't take the time to fully understand the code from as_widget) I'm using it as a temporary hack only. Perhaps there is a better way that involves custom template tags. If so, please let me know.