允许在ModelAdmin上进一步覆盖save_formset

Chr*_*att 7 django django-forms django-admin

非常基本的使用场景.我想保存创建对象的用户和上次修改它的用户.但是,它是一个内联模型,所以我当然需要使用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()
Run Code Online (Sandbox Code Playgroud)

问题是,如果你注意到,因为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)
Run Code Online (Sandbox Code Playgroud)

super更多地调整了出于习惯的调用,而不是认为它实际上会导致formset保存两次.然而,它仍然适用于除了一个之外的每个场景:删除.只要您尝试删除管理员中的内联,就会出现错误.这个错误很模糊,并没有真正与我的问题相关,但我相信这与你刚删除其中一个实例后再次尝试保存formset有关.super删除调用时,代码工作正常.

长和短,有没有什么方法我都缺少自定义formset保存行为允许子类做自己的覆盖?

Yuj*_*ita 5

这是一个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)
Run Code Online (Sandbox Code Playgroud)

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

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