about 2 years ago

I didn't expect creating a radio input in a form using Bootstrap3's style could have some many pitfalls.

Here is a model I have:

class UserProfile(models.Model):
    GENDER_CHOICES = (('M', 'Male',), ('F', 'Female',))
    fullname = models.CharField(u'Name', max_length=10)
    sex = models.CharField(u'Gender', max_length=1, choices=GENDER_CHOICES)
    # ... and 11 other fields

Here is the corresponding ModelForm:

from django.forms.widgets import RadioSelect

class UserProfileForm(ModelForm):
    class Meta:
        model = UserProfile
        widgets = {
            'sex': forms.RadioSelect()

I have a sex field which let users to pick their gender. When running the code above, I get three choices instead of two:

  • '--------' # django automatically insert this 'default' option
  • 'Male'
  • 'Female'

This is of course not what we want. There should only be two options, 'Male' and 'Female'. So to correct this issue, I had to add TWO extra parameters to the Model:

sex = models.CharField(u'Gender', max_length=1, choices=GENDER_CHOICES, blank=False, default='M')

Note that I added blank=False AND default='M'. If you only include blank=False, you will still see the '--------' option. It is important to have both of them set.

Now, I want to style my forms using Bootstrap3. To do this, I recommend using crispy-forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from crispy_forms.bootstrap import InlineRadios

class UserProfileForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(UserProfileForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.method = 'post'
        self.helper.form_action = 'index'
        self.helper.layout.append(Submit('save', 'Save'))

    class Meta:
        model = UserProfile

Crispy-form creates a Bootstrap3 style input field for every single field defined in the model when this line runs:

self.helper = FormHelper(self)

The UserProfile model has 13 fields. It is nice that I don't have to specify each one of the 13 fields. But here comes the problem, when the radio input is been rendered. The male and female options are listed vertically. I want them to be listed horizontally. Crispy-form does provide an inline option for rendering radio inputs horizontally. This is nice, but how can I replace the default Layout of the gender field with the suggested inline Layout?

self.helper[1] = InlineRadios('sex')

Crispy-forms allows you to access the Layout of each field by using index.

With the tweaks above, now I have a wonderful radio input for gender selection :) So many small things need to be taken care of for something seems so trivial.

← Django customize error messages during user registration Django reverse vs reverse_lazy →
comments powered by Disqus