Django modelformset_factory delete modelforms marked for deletion

你离开我真会死。 提交于 2021-02-18 11:21:22

问题


When using modelformset_factory how do you delete objects from the database that get marked for delete in the form?

I create my modelformset_factory like this:

ItemFormset = modelformset_factory(Item, ItemModelForm, extra=1, can_delete=True)
qset = Item.objects.filter(pr=pr)
formset = ItemFormset(queryset=qset)

When the formset comes back in the POST I get the data like so:

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

When the formset comes back I can get all of the objects marked for delete with formset.deleted_forms but I can't figure out how to actually delete them. I've tried looping through each one and deleting each one individually but I get the error: Item object can't be deleted because its id attribute is set to None.

In the template I'm including {{form.id}} so each object has it's ID being passed back in the POST.

After calling instances = formset.save(commit=False) I can call formset.deleted_objects but it's just an empty list: []

Can anyone see what I'm doing wrong that would make the objects not get deleted from the database?


回答1:


What is confusing you is that formset.save(commit=False) doesn't do what you think it does.

Although with commit=False set, edited objects are not save() d, confusingly, deleted objects are deleted.

Therefore, when you loop over marked_for_delete after having called save(commit=False), you're getting objects that have been deleted already, hence the None for their id's.

Your self-answer is better, more idiomatic Django as it happens; in general, one should just call formset.save() and let it default to commit=True. The fact that the commit=False case is relatively rare and disused is probably why nobody has fixed the (IMO, buggy) behavior of deleting objects.

(As an aside, I have only observed this behavior in non-transactional/AutoCommit database environments; it might be that with commit=False and transactions enabled you get a more robust behavior with respect to deletion.)

P.S. - This behavior has been changed in Django 1.7:

"If you call formset.save(commit=False), objects will not be deleted automatically. You’ll need to call delete() on each of the formset.deleted_objects to actually delete them."




回答2:


By including all of the fields from the Item model in the ItemModelForm I was able to call formset.save() and all models marked for delete in the form get deleted and any models that were modified or added get updated or saved. I include the field 'pr' (a foreign key) as a HiddenInput and initialize it by extending ItemModelForm like so:

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)

Then I was able to handle the post like this:

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

Since modelformset_factory accepts a ModelForm class and not an instance of a modelform I had to extend ItemModelForm in the view where I know what I want the pr to be initialized to.



来源:https://stackoverflow.com/questions/9573698/django-modelformset-factory-delete-modelforms-marked-for-deletion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!