setting help_text for each choice in a RadioSelect
Asked Answered
S

4

10

I can set the help_text attribute on any form field, but is it possible to set help_text on the choices used for a RadioSelect()?

I'd looking for a clean way to show some help information under each radio button.

Below is the code for the model and the form, I can render the name attribute in a template with the label, input element and help text. I'd also like to be able to render membership_type attribute with a label ('Membership Type'), radio buttons ('open membership' and 'closed membership'), and help text associated to each radio element ('anyone can join this group' and 'only select members can join this group').

class Group(models.Model):
  MEMBERSHIP_CHOICES = (
    ('O', 'Open membership'),
    ('C', 'Closed membership'),
  )

  name = models.CharField(max_length=255)
  membership_type = models.CharField(max_length=1, choices=MEMBERSHIP_CHOICES, default="O")

class GroupForm(forms.ModelForm):
  name = forms.CharField(label="Group name", help_text="Enter a name for your new group")

  class Meta:
    model = Group
    widgets = { "membership_type": forms.RadioSelect }
Surpassing answered 5/2, 2010 at 2:31 Comment(0)
R
6

@Rishabh is correct but I'll elaborate further as, at first glance, it doesn't appear to be the solution, although it is; or, at least, it can be kludged to get a useful effect without having to dive too deep into django forms.

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

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()
        )
Runnel answered 15/11, 2010 at 12:8 Comment(0)
M
3

For others coming across this 10+ years later, I was able to do this by creating a custom widget that overrides the default template for radio buttons, and passing custom attributes to it.

# Custom widget    
class RadioSelectHelpTextWidget(forms.RadioSelect):
                option_template_name = 'custom_templates/forms/radio_option_help_text.html'
        
# Form class that calls widgets, passed custom attributes to widget
class TemplateCreateForm(ModelForm):
            created_by = forms.ModelChoiceField(required=False,queryset=User.objects.none())
            class Meta:
                model = Template
                fields = ['name', 'type', 'created_by']
                widgets = {
                    'name': forms.TextInput(attrs={'class': 'input'}),
                    'type': RadioSelectHelpTextWidget(
                        attrs={
                            'help_text': {
                                'custom': 'This is custom help text.',
                                'html': 'This is help text for html.'
                            }
                        }
                    )
                }

Template for custom radio buttons (custom_templates/forms/radio_option_help_text.html)

{% load custom_template_filters %}
{% if widget.wrap_label %}
    <label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>
{% endif %}
{% include "django/forms/widgets/input.html" %}
{% if widget.wrap_label %}
    {{ widget.label }}
    {% if widget.attrs.help_text %}
        {% if widget.value in widget.attrs.help_text %}
            <p class="is-size-7">
             {{ widget.attrs.help_text|dict_get:widget.value }}
            </p>
        {% endif %}
    {% endif %}
    </label>
{% endif %}

The result: enter image description here

Marshland answered 6/6, 2022 at 22:4 Comment(0)
S
0

Yo its almost 2024, you can do this now.

For example, in a boolean model field:

models.BooleanField(default=False,help_text="Your help text")

In your form

value = forms.RadioSelect()

Output on page looks exactly as desired.

Strangulate answered 27/8, 2023 at 5:10 Comment(0)
S
-1

Assuming you're using RadioSelect as a widget for forms.ChoiceField, you can do something like:

choices = (('1', 'First help_text here'),
           ('2', 'Second help_text here'),
           ('3', 'Third help_text here'),
          )

class MyForm(forms.Form):
    ...
    choice = forms.ChoiceField(widget = RadioSelect, choices = choices)

This isn't a strict use of help_text but it should get the job done in most cases.

Sourwood answered 5/2, 2010 at 3:18 Comment(3)
In your example you've setup an id and label attribute. I already have both of those, I'm looking to add a third attribute; help_text.Surpassing
Maybe if you showed us some code, we'd be able to help more. Right now, I don't understand what it is you're trying to achieve that cannot be done using the id and label attributes.Sourwood
I updated the question with more information and code snippets, thanks for looking at this.Surpassing

© 2022 - 2024 — McMap. All rights reserved.