Django: Using Radio select box on model formsets

倖福魔咒の 提交于 2019-12-06 15:06:31

I think the key here is that the radio button is not actually part of the formset: it's part of the parent form. It's the actual Album model that needs to know which of the Photo objects is the cover image. So what you want to do is to display each option from the radio button alongside its corresponding line in the Photo formset - and that's the tricky bit, because Django can't render form fields in that way. You'll need to produce the HTML for each option manually.

So, given these forms, and assuming the Album model has a cover_image which is a OneToOneField to Photo:

class AlbumForm(forms.modelForm):
    class Meta:
        model = Album

photo_formset = forms.inlineformset_factory(Album, Photo, form=ProjectGalleryForm)

in the template you would do something like:

{% for photo_form in photo_formset %}
    <tr><td>
    {% if photo_form.instance.pk %}
        <input type="radio" id="id_cover_image_{{ forloop.counter }}" name="cover_image" value="{{ photo_form.instance.pk }}">
        <label for="id_cover_image_{{ forloop.counter }}">Use as cover image</label>
    {% endif %>
    </td><td>{{ photo_form.as_p }}</td>
    </tr>
{% endfor %}

I like to have the a neat template file and therefore, I made a custom widget for this purpose.

class SingleRadioInput(Input):

    input_type = 'radio'
    def render(self, value, checked, attrs=None):
        output = []
        if value:
            is_cover = ''
            if checked : is_cover = 'checked'
            output.append(
                ('<input type="radio" name="inline" value="%s" %s/>') 
                % (value, is_cover)
            )

        return mark_safe(u''.join(output))

Hope it can help someone

Based on @Mikou answer, here is my more comprehensive solution.

In order to keep my template clean and pretty, I used a custom widget

class SingleRadioInput(forms.widgets.Input):
input_type = 'radio'
def render(self, name, value, attrs=None):
    final_attrs = self.build_attrs(attrs, type=self.input_type)
    output = []
    if name:
        is_checked = ''
        if value:
            is_checked = 'checked'
        output.append(
            ('<input id="%s" type="radio" name="%s" value="%s" %s/>')
            % (final_attrs['id'], final_attrs['name'], final_attrs['instance_id'], is_checked )
        )
    return mark_safe(u''.join(output))

My object form looks like that, it will auto select the object if the field default == True

class ObjectForm(forms.ModelForm):

def __init__(self, *args, **kwargs):
    super(ObjectForm, self).__init__(*args, **kwargs)
    self.fields['default'].widget.attrs.update({'instance_id': self.instance.id, 'name': 'default'})
    if self.instance.default:
        self.fields['default'].widget.attrs.update({'value': True})


class Meta:
    model = MyModel
    fields = ['default']
    widgets = {
        'default': SingleRadioInput(),
    }

Here is my formset

ProductReferenceFormset = inlineformset_factory(ParentModel, MyModel,
                                            form=ObjectForm,
                                            extra=0, can_delete=False, can_order=False)

I gave up handling the save part in the form, it is really not worth the complexity I think... So the save part is in the form_valid() in the View

def form_valid(self, form, price_form):
    form.save()
    # save the default radio
    MyModel.objects.filter(parent=self.object).update(default=False)
    MyModel.objects.filter(id=self.request.POST.get('default')).update(default=True)

    return HttpResponseRedirect(self.get_success_url())

Qualification:

<option value='10th' {% if '10th' in i.qf %} selected='select' {% endif %}>10th</option>
<option value='12th' {% if '12th' in i.qf %} selected='select' {% endif %}>12th</option>
<option value='graduted' {% if 'Graduated' in i.qf %} selected='select' {% endif %}>Graduated</option>
 </select>
<br><br>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!