[Answer]-Django – MultipleCheckBoxSelector with m2m field – How to add object instead of save_m2m()



I am confused as to what you’re doing with the ezMap form field. You set its queryset to a single-element list, then use a CheckboxSelectMultiple widget for it. Are you setting up to let the user deselect that matching map, but not add new ones?

To do this at initialization, you need to define a custom base formset class and pass that in as the formset argument to your factory.

from django.forms.models import BaseInlineFormSet

class polygonLayerStyleForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.map_selected =  kwargs.pop("map_selected", None)
        super(polygonLayerStyleForm, self).__init__(*args, **kwargs)
        self.fields['conditionStyle'].help_text = "Put * if you want to select the entire table"
        self.fields['ezMap'].widget = forms.CheckboxSelectMultiple()
        self.fields['ezMap'].queryset = EzMap.objects.filter(id=self.map_selected.id)
        self.fields['ezMap'].help_text =""

    class Meta:
        model = LayerStyle

class polygonLayerStyleFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        self.map_selected =  kwargs.pop("map_selected", None)
        super(polygonLayerStyleFormset, self).__init__(*args, **kwargs)

    def _construct_form(self, i, **kwargs):
        kwargs['map_selected'] = self.map_selected
        return super(polygonLayerStyleFormset, self)._construct_form(i, **kwargs)

ftlStylePolygonFormset = inlineformset_factory(Shapefile, LayerStyle, formset=polygonLayerStyleFormset, form=polygonLaterStyleForm, # and other arguments as above

It might be simpler to just go through the formset forms and directly change the field’s queryset after creating it in your view:

    formset = ftlStylePolygonFormset(instance=layer_selected)
    for form in formset.forms:
        form.fields['ezMap'].queryset = EzMap.objects.filter(id=map_selected.id)

Speaking of which, the usual convention is to split the POST and GET cases in the view:

from django.shortcuts import render

def setLayerStyle(request, map_name, layer_id):
    map_selected = EzMap.objects.get(map_name=map_name, created_by=request.user)
    layer_selected = Shapefile.objects.get(id=layer_id)
    layerStyle_selected = LayerStyle.objects.filter(layer=layer_selected)
    if request.method == 'POST':
        formset = ftlStylePolygonFormset(request.POST, instance=layer_selected, map_selected=map_selected)
        if formset.is_valid():
            instances = formset.save()
            save_link = u"/ezmapping/map/%s" % (map_name)
            return HttpResponseRedirect(save_link)
        formset = ftlStylePolygonFormset(instance=layer_selected, map_selected=map_selected)
    return render(request, "ezmapping/manage_layerStyle.html", {'layer_style': layerStyle_selected, 'layerStyleformset': formset, 'layer': layer_selected})

And it’s better to use the redirect shortcut to reverse lookup a view for your redirect on success rather than hardcode the target URL. And to use get_object_or_404 or some equivalent when accessing objects based on URL arguments – right now a bogus URL will trigger an exception and give the user a 500 status error, which is undesirable.

To conditionally add to the ezMap relationship:

class polygonLayerStyleForm(forms.ModelForm):
    add_to_map = forms.BooleanField()

    def save(self, *args, **kwargs):
        instance = super(polygonLayerStyleForm, self).save(*args, **kwargs)
        instance.add_to_map = self.cleaned_data['add_to-map']
        return instance

Then in the view:

instances = formset.save()
for instance in instances:
    if instance.add_to_map:

You could also do the add call in the save method, but then you’d have to set the map as member data sometime previously – and more importantly, deal with the commit=False case.

Leave a comment