[Django]-How to loop over form field choices and display associated model instance fields

50👍

Easiest would be if you define the whole form in a HTML template. You should be able to iterate over a field’s values in a template like that:

{% for value, text in form.hikers.field.choices %}
    {{ value }}: {{ text }}
{% endfor %}

14👍

Try this solution :

<ul>
{% for choice in form.my_choice_field.field.choices %}
  <li>
    <input type="radio" name="my_choice_field" value="{{choice.0}}"
      {% ifequal form.my_choice_field.data choice.0 %} 
         checked="checked"
      {% endifequal %}/>
    <label for="">{{choice.1}}</label>
 </li>
{% endfor %}
</ul>

see this link : http://www.ilian.io/django-forms-choicefield-and-custom-html-output/

9👍

This is surprisingly tricky, but you can do it using ModelMultipleChoiceField, CheckboxSelectMultiple, and a custom template filter. The form and widget classes get most of the way there, but the template filter works out which widget to give you for each instance in the queryset. See below…

Generic solution

# forms.py
from django import forms
from .models import MyModel

class MyForm(forms.Form):
    my_models = forms.ModelMultipleChoiceField(
                                      widget=forms.CheckboxSelectMultiple,
                                      queryset=None) 

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['my_models'].queryset = MyModel.objects.all()


# myapp/templatetags/myapp.py
from django import template
from copy import copy

register = template.Library()

@register.filter
def instances_and_widgets(bound_field):
    """Returns a list of two-tuples of instances and widgets, designed to
    be used with ModelMultipleChoiceField and CheckboxSelectMultiple widgets.

    Allows templates to loop over a multiple checkbox field and display the
    related model instance, such as for a table with checkboxes.

    Usage:
       {% for instance, widget in form.my_field_name|instances_and_widgets %}
           <p>{{ instance }}: {{ widget }}</p> 
       {% endfor %}
    """
    instance_widgets = []
    index = 0
    for instance in bound_field.field.queryset.all():
         widget = copy(bound_field[index])
         # Hide the choice label so it just renders as a checkbox
         widget.choice_label = ''
         instance_widgets.append((instance, widget))
         index += 1
    return instance_widgets


# template.html
{% load myapp %}     
<form method='post'>
   {% csrf_token %}     
   <table>
       {% for instance, widget in form.job_applications|instances_and_widgets %}
           <tr>
               <td>{{ instance.pk }}, {{ instance }}</td>
               <td>{{ widget }}</td>
           </tr>
       {% endfor %}
   </table>
   <button type='submit' name='submit'>Submit</button>
</form>

Specific to you

It should work if you adjust the form like so:

class ClubForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        cluk_pk = kwargs.pop('club_pk')
        super(ClubForm, self).__init__(*args, **kwargs)
        self.fields['hikers'].queryset = Club.objects.filter(pk=club_pk)

    class Meta:
        model = Club
        fields = ('hikers',)
        widgets = {'hikers': forms.CheckboxSelectMultiple}

4👍

Maybe help someone.

template.html

<!-- radio -->
<div class="form-group">
    {{ form.field_name.label_tag }}
    {% for pk, choice in form.field_name.field.widget.choices %}
    <div class="custom-control custom-radio custom-control-inline">
        <input id="id_{{form.field_name.name}}_{{ forloop.counter0 }}" name="{{form.field_name.name}}" type="{{form.field_name.field.widget.input_type}}" value="{{pk}}" class="custom-control-input"
         {% ifequal form.field_name.data pk.0 %}
           checked="checked"
         {% endifequal %}/>
        <label for="id_{{form.field_name.name}}_{{ forloop.counter0 }}" class="custom-control-label">{{ choice }}</label>
    </div>
    {% endfor %}
</div>

<!-- checkbox -->
<div class="form-group">
    {{ form.field_name.label_tag }}
    {% for pk, choice in form.field_name.field.widget.choices %}
    <div class="custom-control custom-checkbox custom-control-inline">
        <input id="id_{{form.field_name.name}}_{{ forloop.counter0 }}" name="{{form.field_name.name}}" type="{{form.field_name.field.widget.input_type}}" value="{{pk}}" class="custom-control-input"
         {% ifequal form.field_name.data pk.0 %}
           checked="checked"
         {% endifequal %}/>
        <label for="id_{{form.field_name.name}}_{{ forloop.counter0 }}" class="custom-control-label">{{ choice }}</label>
    </div>
    {% endfor %}
</div>

How to custom checkbox and radio in Django using Bootstrap

My result

2👍

Another example of a generic solution (template only):

{% for widget in form.field_name %}
<tr>
    <th>
        <label for="{{widget.id_for_label}}">
            <input type="{{widget.data['type']}}" name="{{widget.data['name']}}" value="{{widget.data['value']}}" {% if widget.data['selected'] %}selected{% endif %} {% for k, v in widget.data['attrs'].items() %} {{k}}="{{v}}" {% endfor %}>
        </label>
    </th>
    <td>
        {{widget.choice_label}}
    </td>
</tr>
{% endfor %}

Explanation:

Basically, you just iterate over form.field_name and there you get an widget like this:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'choice_label', 'data', 'id_for_label', 'parent_widget', 'renderer', 'tag', 'template_name'] ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__html__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'choice_label', 'data', 'id_for_label', 'parent_widget', 'renderer', 'tag', 'template_name'] 

where widget.data contains all the needed info to build the input elements:

{'name': 'field_name', 'value': 1, 'label': 'Field name 1', 'selected': False, 'index': '0', 'attrs': {'id': 'id_field_name_0'}, 'type': 'checkbox', 'template_name': 'django/forms/widgets/checkbox_option.html'} 
👤Jahid

1👍

I think, you can define own widget class inherited from CheckboxSelectMultiple with your own render() method and customize html output.
See source code, line 690

It’ll be reusable in any template as well.

👤Alexey

1👍

This answer provides a custom form widget – TableSelectMultiple – which sounds like what you want:

There’s also the original Django Snippet.

1👍

related ticket: https://code.djangoproject.com/ticket/9230

I made a widget that makes such a table: http://skyl.org/log/post/skyl/2011/01/wherein-the-inner-workings-of-the-deathstarwidget-are-revealed/

I’m here looking for a better solution still though 😀

Leave a comment