7

这里非常基本的使用场景。我想保存创建对象的用户和最后修改它的用户。但是,它是一个内联模型,所以我当然需要使用save_formset. Django 文档有以下示例代码:

class ArticleAdmin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            instance.user = request.user
            instance.save()
        formset.save_m2m()

问题是,如果你注意到,因为super从来没有被调用,这是一个死胡同。如果ModelAdmin是子类并且此方法以相同的方式被覆盖,您将失去父级中固有的功能。这很重要,因为这是一个常见的使用场景,我想将功能分解出来,所以我创建了以下内容:

class TrackableInlineAdminMixin(admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            if hasattr(instance, 'created_by') and hasattr(instance, 'modified_by'):
                if not instance.pk:
                    instance.created_by = request.user
                instance.modified_by = request.user
            instance.save()
        formset.save_m2m()
        super(TrackableInlineAdminMixin, self).save_formset(request, form, formset, change)

super出于习惯,我比其他任何事情都更倾向于呼吁,没想到它实际上会导致表单集保存两次。尽管如此,它仍然适用于所有场景,除了一个:删除。一旦您尝试在管理员中删除内联,您就会收到错误消息。该错误非常模糊,与我在这里的问题并不真正相关,但我相信它与您刚刚删除其中一个实例后尝试再次保存表单集有关。当调用super被删除时,代码工作得很好。

总而言之,我是否缺少任何方法来自定义表单集保存行为允许子类进行自己的覆盖?

4

2 回答 2

6

这是一个doozie。

我玩得很开心,似乎所有的动作都发生在这里django.forms.models.BaseModelFormSet

问题是ModelFormSet.save()无论标志如何都会删除实例,commit并且不会修改表单以反映已删除状态。

如果save()再次调用,它会遍历表单并在ModelChoiceField清理中尝试提取引用的 ID 并引发无效选择错误。

def save_existing_objects(self, commit=True):
    self.changed_objects = []
    self.deleted_objects = []
    if not self.initial_forms:
        return []

    saved_instances = []
    for form in self.initial_forms:
        pk_name = self._pk_field.name
        raw_pk_value = form._raw_value(pk_name)

        # clean() for different types of PK fields can sometimes return
        # the model instance, and sometimes the PK. Handle either.
        pk_value = form.fields[pk_name].clean(raw_pk_value) 
        pk_value = getattr(pk_value, 'pk', pk_value)

        obj = self._existing_object(pk_value)
        if self.can_delete and self._should_delete_form(form):
            self.deleted_objects.append(obj)
            obj.delete()  
            # problem here causes `clean` 6 lines up to fail next round

            # patched line here for future save()
            # to not attempt a second delete
            self.forms.remove(form)

我能够解决此问题的唯一方法是修补BaseModelFormset.save_existing_objects以在删除对象时删除表单self.forms

做了一些测试,似乎没有任何不良影响。

于 2012-04-28T01:26:11.970 回答
0

@Chris Pratt 帮助:

出于习惯,我对 super 的调用比其他任何事情都多,没想到它实际上会导致表单集保存两次。

我试图进一步覆盖 asave_formset以发送保存后信号。我只是无法理解调用super()只是第二次保存表单集。

为了解决这个super()问题,我用我的自定义代码创建了一个方法,当我通过孩子save_formset_now覆盖时我会调用它。save_formsetadmin.ModelAdmin

这是代码,它似乎也处理了删除问题,在 2016 年使用 Django 1.10。

class BaseMixinAdmin(object):
    def save_formset_now(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for obj in formset.deleted_objects:
            obj.delete()
        for instance in instances:
            # *** Start Coding for Custom Needs ***
                ....
            # *** End Coding for Custom Needs ***
            instance.save()
        formset.save_m2m()

class BaseAdmin(BaseMixinAdmin, admin.ModelAdmin):
    def save_formset(self, request, form, formset, change):
        self.save_formset_now(request, form, formset, change)


class ChildAdmin(BaseAdmin):
    def save_formset(self, request, form, formset, change):
        self.save_formset_now(request, form, formset, change)
        my_signal.send(...)
于 2016-09-17T17:15:08.947 回答