开发者

Align radio buttons horizontally in django forms

HI

I want to align the radio buttons horizontall开发者_C百科y. 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


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),
                                 )


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.


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'})
                )

Align radio buttons horizontally in django forms


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")],)},
    }


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>


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

Align radio buttons horizontally in django forms

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

Align radio buttons horizontally in django forms

Not tested for the Django-grappelli, yet.


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'
    })
)


There is actually no need to override the widget, template or anything else in admin. At least since 2008 (see forms.css), the easiest way is to pass a class attribute inline: attrs={'class': 'inline'}

In a custom form, it may look like:

field = forms.ChoiceField(choices=(('1', 'one'), ('2', 'two')),
                          widget=forms.RadioSelect(attrs={'class': 'inline'}))

… and it works the very same for checkboxes:

field = forms.MultipleChoiceField(choices=(('1', 'one'), ('2', 'two')),
                                  widget=forms.CheckboxSelectMultiple(attrs={'class': 'inline'}))

And it should be the very same for formfield overrides, either via ModelAdmin formfield_overrides or formfield_for_* functions.


as renerer raise this error for me:

AttributeError: type object 'RadioSelect' has no attribute 'renderer'

I come up with this code:

<form method="post">
    {% csrf_token %}
    {% for question in form %}
        <p>{{ question.label }}: </p>
        {% for choice in question %}
            {{ choice }}
        {% endfor %}
    {% endfor %}
    <br><br>
    <button type="submit">
            submit
    </button>

</form>


You can use this approach. In my opinion, it is the simplest

class YourAdminForm(forms.ModelForm):

    class Meta:
        model = models.YourModel
        widgets = {
            "your_field": forms.RadioSelect(attrs={"class": "inline"}),
        }

or just define your field something like this

    your_field = forms.ChoiceField(
        choices=(...),
        widget=forms.RadioSelect(attrs={"class": "inline"}),
    )


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 }}


I had to figure this out for a CMS I manage. Some of the answers here are close, but aren't completely right. You need to provide the class in the attrs parameter, as at least one other response has mentioned, but you should provide inline li. If you provide style, then the style will get applied to the the input element, instead of the li.

I couldn't find anything in their documentation that lists the available CSS classes for their components, so I discovered this by using DevTools in Firefox to look at the available styles, and then finding inline li in their forms.css.

This solution makes it possible change the alignment without needing to mess with templates or making my own CSS file.

from django import forms

from .models import MyModel

MyForm = forms.modelform_factory(
    MyModel,
    widgets={"field_name": forms.RadioSelect(attrs={"class": "inline li"})},
    fields="__all__",
)

To figure out what other classes are available so you can more conveniently modify the the layout of various form elements, run python manage.py collectstatic (make sure STATIC_ROOT is defined), and you can find forms.css under STATIC_ROOT/static/admin/css.

In this particular repo, I have STATIC_ROOT set to ./lib in my local_settings.py:

$ python manage.py collectstatic

1408 static files copied to './lib/static'.

$ ls -1 lib/static/admin/css/
autocomplete.css
base.css
changelists.css
dashboard.css
fonts.css
forms.css
login.css
nav_sidebar.css
responsive.css
responsive_rtl.css
rtl.css
vendor/
widgets.css

Once I actually got it displaying horizontally with a clean solution, I also wanted to hide the blank option from the list of choices, so ended up creating my own CSS file to do that, since the documentation for that was easy to find

/* BASE_DIR/static/css/my_customizations.css */
#id_field_name li:has(input[value='']) {
  visibility: hidden;
  padding: 0;
  margin: 0;
  height: 0;
}
from django import forms

from .models import MyModel

class MyForm(
    forms.modelform_factory(
        MyModel,
        widgets={"field_name": forms.RadioSelect(attrs={"class": "inline li"})},
        fields="__all__",
    )
):
    class Media:
        css = {"all": ("css/my_customizations.css",)}

(Though, as of January 2023, :has is not yet supported by Firefox, but will hopefully be supported sometime this year)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜