Murali Mopuru
Murali Mopuru

Reputation: 6570

Django forms - checkbox group with dropdowns

Can someone tell me how to write CustomWidget/CustomField for a checkbox group with drop-downs in it using Django forms?

  1. All checkboxes should have the same name (Checkbox group)
  2. Each checkbox should have a drop down as child element (same dropdown for all checkboxes)
  3. CustomWidget/CustomField should accept options= [] param to generate checkbox group
  4. Checkbox group with required=True validation

enter image description here

Ex. Case: Text boxes will show Dimensions and each Dimension should have a drop down with list of aggregate functions in it.

Upvotes: 1

Views: 2117

Answers (3)

Murali Mopuru
Murali Mopuru

Reputation: 6570

In forms.py

class DynamicForm(forms.Form):

    options = [
    ('Textbox1', 'Textbox1'),
    ('Textbox2', 'Textbox2'),
    ...
    ]

    dynamic_data = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=options)

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

        for i in range(1,len(self.options)+1):
            self.fields['dd_func_'+str(i)] = forms.ChoiceField(widget=forms.Select, choices= [
                            ('average', 'average'),
                            ('sum', 'sum')], initial='sum', required=False)

    def __getitem__(self, name):
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return forms.forms.BoundField(self, field, name)

In views.py

from app.forms import DynamicForm

def create_form(request):
    if request.method == 'POST':
        dynamic_form = DynamicForm(request.POST)
        ...
        ...
    else:
        dynamic_form = DynamicForm()
    return render(request, 'form.html', {
        'dynamic_form': dynamic_form
        })

In templates/form.html

{% load myfilters %}
{{ dynamic_form.dynamic_data.errors }}
{% for checkbox in dynamic_form.dynamic_data|checkboxiterator %} 
    {{ checkbox }}
    {% with c=forloop.counter|stringformat:"s" %}
        {% with c='dd_func_'|add:c %}
            {{ dynamic_form|lookup:c }}
        {% endwith %}
    {% endwith %}
{% endfor %}

In templatetags/myfilters.py

https://djangosnippets.org/snippets/2159/

and

@register.filter
def lookup(f, name):
    try:        
    return f[name]
    except KeyError:
    return None

Upvotes: 0

dukebody
dukebody

Reputation: 7185

I'd use MultiValueField and MultiWidget.

You can make the MultiValueField init method accept any options you want to build all the options and use custom logic in the MultiWidget to render all the checkboxes and selects and compress/decompress the values input by the user to a suitable representation, like a dict {checkbox_name: operation}.

Upvotes: 1

DGDD
DGDD

Reputation: 1390

If I understand you correctly, this should do it.

Depending on what you have in your views, simply place some list in your_list as context. Depending on how many items there are in the list, it will create one of these checkbox-list combinations for each one.

{% for number in your_list %}
    <div class="check_box" id="check_box_{{ number }}">
        <input id="cb_{{ number }}" name="cb_{{ number }}" type="checkbox"><label for="cb_{{ number }}">textbox {{ number }}</label>
        <select class="some_class">
            <option value="1">Sum</option>
            <option value="2">Difference</option>
            <option value="3">Whatever</option>
        </select>
    </div>
{% endfor %}

The output will look like this: http://jsfiddle.net/3vetooLp/1/

Upvotes: 0

Related Questions