django form radio input layout

前端 未结 4 849
醉话见心
醉话见心 2021-01-02 17:05

What is the \"djangoy\" way to approach this problem:

In my form class, I have a forms.ChoiceField whose widget is a forms.RadioSelect widget, one of whose choices n

相关标签:
4条回答
  • 2021-01-02 17:39

    Anton's answer worked, and was a decent answer for a while there - but unfortunately it became unmaintainable. So, taking a cue from a diff attached to django ticket #9230, I just monkey patched django.forms.forms.BoundField

    from django import forms
    
    def MonkeyPatchDjangoFormsBoundField():
        def prepare_widget_render(self, widget=None, attrs=None, only_initial=False):
            """
            Prepare the data needed for the widget rendering.
            """
            if not widget:
                widget = self.field.widget
    
            attrs = attrs or {}
            auto_id = self.auto_id
            if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
                if not only_initial:
                    attrs['id'] = auto_id
                else:
                    attrs['id'] = self.html_initial_id
    
            if not only_initial:
                name = self.html_name
            else:
                name = self.html_initial_name
    
            return widget, name, attrs
    
        def as_widget(self, widget=None, attrs=None, only_initial=False):
            """
            Renders the field by rendering the passed widget, adding any HTML
            attributes passed as attrs.  If no widget is specified, then the
            field's default widget will be used.
            """
            widget, name, attrs = self.prepare_widget_render(widget, attrs, only_initial)
            return widget.render(name, self.value(), attrs=attrs)
    
        def __iter__(self):
            """
            Check if current widget has a renderer and iterate renderer.
            """
            widget, name, attrs = self.prepare_widget_render()
            if not hasattr(widget, 'get_renderer'):
                raise Exception, "Can not iterate over widget '%s'" % widget.__class__.__name__
            renderer = widget.get_renderer(name, self.value(), attrs=attrs)
            for entry in renderer:
                yield entry
    
        def __getitem__(self,idx):
            """
            Tries to use current widget's renderer, and then check attribute.
            """
            widget, name, attrs = self.prepare_widget_render()
            try:
                renderer = widget.get_renderer(name, self.value(), attrs=attrs)
                return renderer[idx]
            except Exception:
                return getattr(self,idx)
    
        forms.forms.BoundField.prepare_widget_render = prepare_widget_render
        forms.forms.BoundField.as_widget = as_widget
        forms.forms.BoundField.__iter__ = __iter__
        forms.forms.BoundField.__getitem__ = __getitem__
    

    This allowed me to be able to access the radio inputs directly, by using {{ form.field.0.tag }}, or through iteration - {% for radio in form.field %} {{ radio.tag }} {% endfor %}. Much easier to take care of!

    0 讨论(0)
  • 2021-01-02 17:42

    I would do this by subclassing RadioFieldRenderer and attaching it to a custom widget:

    # forms.py
    from django import forms
    from django.forms.widgets import RadioSelect, RadioFieldRenderer
    from django.template.loader import render_to_string
    from myapp.models import RatherComplicatedModel
    
    
    class MyRadioFieldRenderer(RadioFieldRenderer):
        def render(self):
            return render_to_string(
                'my_radio_widget.html',
                        {'field': self})
    
    
    class MyRadioSelect(RadioSelect):
        renderer = MyRadioFieldRenderer
    
    
    class RatherComplicatedForm(forms.ModelForm):
        RADIO_CHOICES = (
            ('none', "No Textbox"),
            ('one', "One Textbox: "),
        )
        rad = forms.ChoiceField(widget=MyRadioSelect(),choices=RADIO_CHOICES)
    
        class Meta:
            model = RatherComplicatedModel
    

    Then the template:

    #my_radio_widget.html
    <ul>
        {% for choice in field %}
            <li>
                <label for="id_{{ field.name }}_{{ forloop.counter0 }}">
                    <input type="radio"
                           name="{{ field.name }}"
                           value="{{ choice.choice_value }}"
                           id="id_{{ field.name }}_{{ forloop.counter0 }}"
                           {% if field.value == choice.choice_value %}
                               checked='checked'
                           {% endif %}/>
                    {{ choice.choice_label }}
                </label>
            </li>
        {% endfor %}
    </ul>
    
    0 讨论(0)
  • 2021-01-02 17:45

    If I understand your problem correctly, you can access choices tuple in template:

    <ul>
        {# Assuming {{ field }} here is {{ form.rad }} #}
        {% for choice in field.field.choices %}
        <li>
            <label for="id_{{ field.html_name }}_{{ forloop.counter0 }}">
                <input type="radio"
                    id="id_{{ field.html_name }}_{{ forloop.counter0 }}"
                    value="{{ choice.0 }}"
                    name="{{ field.html_name }}" />
                {{ choice.1 }}
                {% if choice.0 == 'one' %}
                    {# Necessary field here #}
                    {{ form.bar }}
                {% else %}
                    No Textbox
                {% endif %}
            </label>
        </li>
        {% endfor %}
    </ul>
    
    0 讨论(0)
  • 2021-01-02 17:54

    Choices should be in the Model:

    class RatherComplicatedModel(models.Model):
        BAR_CHOICES = (
            (0, "No Textbox"),
            (1, "One Textbox: "),
        )
        #some other stuff...
        bar = models.IntegerField(blank=True, null=True, choices=BAR_CHOICES)
    

    Then just:

    class RatherComplicatedForm(forms.ModelForm):
        #various and sundry code...
        bar = forms.ChoiceField(widget=forms.RadioSelect(), 
                     choices=RatherComplicatedModel.BAR_CHOICES)
        class Meta:
            model = RatherComplicatedModel
    
    0 讨论(0)
提交回复
热议问题