17

HI

I want to align the radio buttons horizontally. By default django forms displays in vertical format.

feature_type  = forms.TypedChoiceField(choices = formfields.FeatureType, widget = forms.RadioSelect)

Is there any special parameter that we can pass for radio buttons alignment?

Thanks in advance

vkrams
  • 7,267
  • 17
  • 79
  • 129
  • 2
    I think this is more of a CSS problem than Django. You can perhaps add a class to the radio buttons and say float:left; in your css for radio button class. –  May 09 '11 at 10:36
  • 1
    I have googled it and I have found one link where I could not able to make it work. https://wikis.utexas.edu/display/~bm6432/Django-Modifying+RadioSelect+Widget+to+have+horizontal+buttons. I will try to add the css and will let you know if it fixes. thanks geekam – vkrams May 09 '11 at 11:01

12 Answers12

20

Thats the behavior of the RadioField. If you want it rendered horizontally, create a horizontal renderer, like something as follows:

from django.utils.safestring import mark_safe

class HorizontalRadioRenderer(forms.RadioSelect.renderer):
  def render(self):
    return mark_safe(u'\n'.join([u'%s\n' % w for w in self]))


class ApprovalForm(forms.Form):
    approval = forms.ChoiceField(choices=APPROVAL_CHOICES,
                 initial=0,
                 widget=forms.RadioSelect(renderer=HorizontalRadioRenderer),
                                 )
HighLife
  • 4,218
  • 7
  • 40
  • 56
lprsd
  • 84,407
  • 47
  • 135
  • 168
  • Hey lakshman, when I execute the above code its getting error `mark_safe` is not defined. If you can help this that would be agreat help – vkrams May 10 '11 at 05:29
  • You should import that from Django.templates – lprsd May 10 '11 at 06:27
  • 1
    Cool its working fine laxman. Thanks for the help :-) for mark_safe to work I have imported: `from django.utils.safestring import mark_safe` – vkrams May 10 '11 at 10:56
  • 22
    Since Django 1.11, this gives me an error at ``forms.RadioSelect.renderer``: "type object 'RadioSelect' has no attribute 'renderer'". – mimo Apr 05 '17 at 04:58
  • I got the same error "type object 'RadioSelect' has no attribute 'renderer'" .any help please – Elroum Jun 12 '18 at 09:46
  • this does not work in newer versions of Django and it isn't the right way to go anyway. Check out the answer below that changes the styling of ul -> li which should be selected as the correct answer. – Werner Trelawney Sep 05 '21 at 15:20
6

Another way out is to changing the style of ul->li list to display:inline-block. You can do something like that

 <style>
 ul#youelementId li{
  display: inline-block;
  }
</style>

hope this would help next reader.

Maryam Tariq
  • 61
  • 1
  • 1
6

I've come up with an alternative solution. If you are using bootstrap to render your forms, you can add the .form-check-inline class to the input and the field will display horizontally. Listed below is code that shows what I'm describing. I hope this helps someone from reinventing the wheel. Thanks for reading. Take care and have a good day.

                feature_type = forms.MultipleChoiceField(
                required=False,
                ...
                widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-inline'})
                )

This image shows a set of radio buttons displayed horizontally.

JeremyG
  • 179
  • 2
  • 6
2

I don't know why people suggest so difficult decisions, that don'ts work for my Django 4.1 xD. Solution:

{% for choice in form.feature_type %}
    {{ choice }}
{% endfor %}

# Also you can use:
{{ choice.tag }}
{{ choice.choice_label }}
Dyno
  • 21
  • 2
1

On my Django 2.2.6 above solutions did not worked well, so I post my solution after many tries and following the breadcrumbs until the django forms widget templates used.

I had to override 2 templates, and heritage my own widget class and then point it.

The modified default django templates have been:

  • django/forms/templates/django/forms/widgets/input_option.html
  • django/forms/templates/django/forms/widgets/multiple_input.html

Now they are:

PROJECT_NAME/PROJECT_APP/templates/admin/horizontal_option.html

{% if widget.wrap_label %}<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} class="radio-inline">{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}</label>{% endif %}

PROJECT_NAME/PROJECT_APP/templates/admin/horizontal_radios.html

{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
  <li>{{ group }}
    <ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
    {% include option.template_name with widget=option %}{% endfor %}{% if group %}
  </ul></li>{% endif %}{% endfor %}
</ul>{% endwith %}
  • The first one includes a hardcoded class: class="radio-inline" at labels, which in default Django had nothing
  • The second one the rendering of the set of radios, I removed the extra HTML li tags they were rendered inside the internal ul tag.

Then you need to create your own widget class:

from django.forms import RadioSelect


class HorizontalRadioSelect(RadioSelect):
    template_name = 'admin/horizontal_radios.html'
    option_template_name = 'admin/horizontal_inputs.html'

And finally, in my case, I pointed to it overriding formfield_overrides class attribute in my admin. But you can do this in your models too I think:

    formfield_overrides = {
        models.BooleanField: {'widget': HorizontalRadioSelect(choices=[(True, "Yes"), (False, "No"), (None, "Unknown")],)},
    }
miguelfg
  • 1,455
  • 2
  • 16
  • 21
1

Adding this for those using django-crispy-forms and rendering fields with |as_crispy_field. None of the solutions above worked for me, but this did:

Assuming you have something like this in your form:

class MyModelForm(forms.ModelForm):
    class Meta: 
        model = MyModel
        fields = ['your_field_name']

    your_field_name = forms.ChoiceField(widget=forms.RadioSelect(), choices=YOUR_CHOICE_LIST)

...and this in your template:

<div class="row g-3">
    <div class="col-md-12">
        {{ form.your_field_name|as_crispy_field }}
    </div>
</div>

The following CSS does the trick:

<style>
    #div_id_your_field_name div {
        display:inline-flex;
        width: 100%;
    } /* Renders the radio buttons inline and spreads across your div */
    
    #div_id_your_field_name .form-check-label {
        margin-left: 10px;

    } /* puts a bit of space between the radio and label */
    
    #div_id_your_field_name .form-label {
        display: none;

    } /* Optional, if you want to suppress the default label for some reason */
</style>
Milo Persic
  • 985
  • 1
  • 7
  • 17
  • 1
    thanks, this worked (unlike every other solution presented on this page -- I assume because I'm using Crispy Forms) – m.arthur Oct 26 '22 at 19:18
  • This is probably the reason, yes. Glad it helped! (And best wishes on the career transition - you won't regret it!) – Milo Persic Oct 26 '22 at 23:54
0

Modified forms.RadioSelect:

class HorizontalRadioSelect(forms.RadioSelect):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        css_style = 'style="display: inline-block; margin-right: 10px;"'

        self.renderer.inner_html = '<li ' + css_style + '>{choice_value}{sub_widgets}</li>'

Working with on the Python 3.4 with the standart admin of the Django 1.10 enter image description here

and the Django-Suit(http://djangosuit.com/) (it use Bootstrap`s 2 styles)

enter image description here

Not tested for the Django-grappelli, yet.

PADYMKO
  • 4,217
  • 2
  • 36
  • 41
  • This answer isn't directly applicable to the example code provided by the user and, as such, doesn't answer their question. – Jim Jun 26 '19 at 23:03
  • On Django 4.2 (and possibly before), this says, `'HorizontalRadioSelect' object has no attribute 'renderer'` – dfrankow Jun 26 '23 at 13:59
0

According to the Django docs 'attrs' is a dictionary containing HTML attributes to be set on the rendered widget.

With that in mind a simple form based solution would be something like the following:

feature_type  = forms.TypedChoiceField(
    choices = formfields.FeatureType,
    widget = forms.RadioSelect(attrs={
        'style': 'display: inline-block'
    })
)
Yup.
  • 1,883
  • 21
  • 18
  • 3
    There is no "display" attribute, this should be `"style": "display: inline-block"` and it will not work this way. `display: inline-block` will be applied to the radio button instead of `
  • `
  • – Alex Paramonov Jun 14 '18 at 09:19