Django - 具有m2m字段的MultipleCheckBoxSelector - 如何添加对象而不是save_m2m()

时间:2013-08-09 19:11:46

标签: django django-forms

我将inlineformset_factory与自定义表单选项一起使用,以便更改m2m字段的查询集和窗口小部件,即:ezMap。我希望表单为用户提供使用CheckBoxSelectMultiple小部件向m2m字段添加或删除当前selected_map的选项。但是,我不想让用户能够删除已存在的其他对象。问题是当我使用formset.save_m2m()保存formset时,它覆盖了字段并删除了已保存的所有对象。

如何在不删除其他对象的情况下添加新对象?

enter image description here


模型:(删除了一些不必要的字段)

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 to display', null=True, blank=True)

class LayerStyle(models.Model):
    styleName = models.SlugField(max_length=50)
    layer = models.ForeignKey(Shapefile)
    ezMap = models.ManyToManyField(EzMap)

形式:

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 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

    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)

的观点:

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()
            for 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 for add_to_map
        for 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}

1 个答案:

答案 0 :(得分:1)

我对您使用ezMap表单字段所做的事情感到困惑。您将其查询集设置为单元素列表,然后使用CheckboxSelectMultiple小部件。您是否设置让用户取消选择匹配的地图,但不添加新地图?

要在初始化时执行此操作,您需要定义一个自定义基本formset类,并将其作为formset参数传递给您的工厂。

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
                                               )

在视图中创建后,可以更简单地浏览formset表单并直接更改字段的查询集:

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

说到这一点,通常的惯例是在视图中拆分POST和GET案例:

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})

最好使用redirect快捷方式在成功时反向查找重定向视图,而不是硬编码目标网址。并且在基于URL参数访问对象时使用get_object_or_404或某些等价物 - 现在伪造的URL将触发异常并给用户500状态错误,这是不可取的。

有条件地添加到ezMap关系:

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

然后在视图中:

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

您也可以在save方法中执行add调用,但之后您必须将地图设置为成员数据 - 更重要的是,处理commit=False案例。< / p>