这里非常基本的使用场景。我想保存创建对象的用户和上次修改它的用户。但是,它是一个内联模型,所以我当然需要使用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
,而不是认为它实际上会导致formset保存两次。然而,它仍然适用于除了一个之外的每个场景:删除。只要您尝试删除管理员中的内联,就会出现错误。这个错误很模糊,并没有真正与我的问题相关,但我相信这与你刚删除其中一个实例后再次尝试保存formset有关。 <!1}}的调用被删除后,代码就可以了。
多长时间,是否有任何方法我都缺少自定义formset保存行为和允许子类做自己的覆盖?
答案 0 :(得分: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)
我能解决此问题的唯一方法是修补BaseModelFormset.save_existing_objects
,以便在删除对象时从self.forms
删除表单。
进行了一些测试并且似乎没有任何不良影响。
答案 1 :(得分:0)
@Chris Pratt帮助:
我提出了超越习惯的呼吁,而不是其他任何事情, 不认为它实际上会导致formset保存两次。
我试图进一步覆盖save_formset
以发送保存后信号。我只是无法理解调用super()
只是第二次保存formset。
为了处理super()
问题,我使用自定义代码创建了save_formset_now
方法,当我通过save_formset
子代覆盖admin.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(...)