Django modelformset_factory删除标记为删除的模型

nat*_*tes 7 django django-forms

当使用modelformset_factory时,如何从数据库中删除在表单中标记为删除的对象?

我像这样创建我的modelformset_factory:

ItemFormset = modelformset_factory(Item, ItemModelForm, extra=1, can_delete=True)
qset = Item.objects.filter(pr=pr)
formset = ItemFormset(queryset=qset)
Run Code Online (Sandbox Code Playgroud)

当formset在POST中返回时,我得到如下数据:

if request.method == "POST":
    formset = ItemFormset(request.POST,queryset=qset)
    if  formset.is_valid():
        marked_for_delete = formset.deleted_forms
        instances = formset.save(commit=False)
        for item in instances:
            item.pr = pr
            item.save()
Run Code Online (Sandbox Code Playgroud)

当formset回来时,我可以获得所有标记为删除的对象,formset.deleted_forms但我无法弄清楚如何实际删除它们.我已经尝试循环遍历每个并单独删除每个但我得到错误:Item object can't be deleted because its id attribute is set to None.

在我所包含的模板中{{form.id}},每个对象都有它在POST中传回的ID.

打电话后instances = formset.save(commit=False)我可以打电话formset.deleted_objects但它只是一个空列表:[]

任何人都可以看到我做错了会使对象无法从数据库中删除吗?

小智 6

令你感到困惑的是,你formset.save(commit=False)做的并不像你想象的那样.

虽然使用commit=Falseset,编辑的对象不是save()d,但是会删除被删除的对象.

因此,当您marked_for_delete在调用之后循环时save(commit=False),您将获得已经删除的对象,因此None它们的id 已经被删除.

你的自我回答是更好的,更恰当的Django,因为它发生; 一般来说,应该只调用formset.save()并默认为commit=True.commit=False案件相对罕见和废弃的事实可能是没有人修复删除对象的(IMO,buggy)行为的原因.

(顺便说commit=False一句,我只在非事务性/ AutoCommit数据库环境中观察到这种行为;可能是在启用了事务并且启用了事务时,您会获得更强大的删除行为.)

PS - 在Django 1.7中更改此行为:

"如果你调用formset.save(commit = False),对象将不会被自动删除.你需要在每个formset.deleted_object上调用delete()来实际删除它们."

  • 请注意,此行为已在[Django 1.7]中更改(https://docs.djangoproject.com/en/1.7/topics/forms/formsets/#can-delete):"如果您调用`formset.save(commit =错误)`,对象不会被自动删除.你需要在每个`formset.deleted_objects`上调用`delete()`来实际删除它们. (2认同)

nat*_*tes 3

通过将 Item 模型中的所有字段包含在 ItemModelForm 中,我能够调用formset.save()并删除表单中标记为删除的所有模型,并且更新或保存任何修改或添加的模型。我将字段“pr”(外键)包含为 HiddenInput 并通过扩展 ItemModelForm 来初始化它,如下所示:

class EnhancedItemForm(ItemModelForm):
    def __init__(self, *args, **kwargs):
        super(EnhancedItemForm, self).__init__(*args, **kwargs)
        self.fields['pr'].widget = forms.HiddenInput()
        self.fields['pr'].initial = pr
ItemFormset = modelformset_factory(Item, EnhancedItemForm, extra=extra_forms, can_delete=True)
formset = ItemFormset(queryset=qset)
Run Code Online (Sandbox Code Playgroud)

然后我就可以像这样处理这个帖子:

if request.method=="POST":
    formset = ItemFormset(request.POST)
    if formset.is_valid():
        # Save, delete, update ..everything you need in one command:
        instances = formset.save()

        for instance in instances:
            # Make sure the assigned pr hasn't changed
            if instance.pr != pr:
                instance.pr = pr 
                instance.save()
Run Code Online (Sandbox Code Playgroud)

由于 modelformset_factory 接受 ModelForm 类而不是 modelform 的实例,因此我必须在视图中扩展 ItemModelForm,在该视图中我知道要将 pr 初始化为什么。