Django - MultipleCheckBoxSelector with m2m field - How to add object instead of save_m2m() -
i use inlineformset_factory custom form option in order change queryset , widget of m2m field, ie: ezmap
. want form give user option add or remove current selected_map
m2m field checkboxselectmultiple widget. however, dont want give user ability remove other objects there. problem when save formset formset.save_m2m()
, overides field , erase objects saved.
how add new object without erasing others?
models: (some of unecessary fields removed)
class shapefile(models.model): filename = models.charfield(max_length=255) class ezmap(models.model): map_name = models.slugfield(max_length=50) layers = models.manytomanyfield(shapefile, verbose_name='layers display', null=true, blank=true) class layerstyle(models.model): stylename = models.slugfield(max_length=50) layer = models.foreignkey(shapefile) ezmap = models.manytomanyfield(ezmap)
forms:
class polygonlayerstyleformset(forms.modelform): add_to_map = forms.booleanfield(required=false) def __init__(self, *args, **kwargs): self.map_selected = kwargs.pop("map_selected", none) super(polygonlayerstyleformset, self).__init__(*args, **kwargs) self.fields['conditionstyle'].help_text = "put * if want select 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 def save(self, *args, **kwargs): instance = super(polygonlayerstyleformset, self).save(*args, **kwargs) instance.add_to_map = self.cleaned_data['add_to_map'] return instance ftlstylepolygonformset = inlineformset_factory(shapefile, layerstyle, can_delete=true, extra=1, max_num=5, fields = ['stylename', 'conditionstyle', 'fillcolor', 'fillopacity', 'strokecolor', 'strokeweight', 'ezmap'], form=polygonlayerstyleformset)
views:
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) styleformset = ftlstylepolygonformset if request.post: formset = styleformset(request.post, instance=layer_selected) if formset.is_valid(): instances = formset.save() instance in instances: if instance.add_to_map: instance.ezmap.add(map_selecte) else: instance.ezmap.remove(map_selected) save_link = u"/ezmapping/map/%s" % (map_name) return httpresponseredirect(save_link) else: formset = styleformset(instance=layer_selected) #set initial data add_to_map form in formset: if form.instance.pk: if map_selected in form.instance.ezmap.all(): form.fields['add_to_map'].initial = {'add_to_map': true}
i confused you're doing ezmap
form field. set queryset single-element list, use checkboxselectmultiple widget it. setting let user deselect matching map, not add new ones?
to @ initialization, need define custom base formset class , pass in formset
argument 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 want select 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, # , other arguments above )
it might simpler go through formset forms , directly change field's queryset after creating in view:
formset = ftlstylepolygonformset(instance=layer_selected) form in formset.forms: form.fields['ezmap'].queryset = ezmap.objects.filter(id=map_selected.id)
speaking of which, usual convention split post , cases in 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) else: 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 use redirect
shortcut reverse lookup view redirect on success rather hardcode target url. , use get_object_or_404
or equivalent when accessing objects based on url arguments - right bogus url trigger exception , give user 500 status error, undesirable.
to conditionally add 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 view:
instances = formset.save() instance in instances: if instance.add_to_map: instance.ezmap.add(map_selected)
you add
call in save method, you'd have set map member data sometime - , more importantly, deal commit=false
case.
Comments
Post a Comment