0

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

我怎么能只添加一个新对象而不删除其他对象?

在此处输入图像描述


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

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

1 回答 1

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 = 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 参数访问对象时使用或类似的东西 - 现在一个虚假的 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)

您也可以add在 save 方法中进行调用,但是您必须在以前的某个时间将地图设置为成员数据 - 更重要的是,处理这种commit=False情况。

于 2013-08-09T21:09:46.680 回答