2012-04-27 45 views
7

非常基本的使用场景在这里允许save_formset的进一步压倒一切的。我想保存创建对象的用户和上次修改对象的用户。然而,这是一个内联的模型,所以我当然需要使用save_formset。 Django文档具有下面的示例代码:上的ModelAdmin

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保存两次。尽管如此,除了一种情况外,它仍然适用于每种情况:删除只要您尝试在管理员中删除内联,就会收到错误消息。错误的很模糊,并没有真正relavent在这里我的问题,但我相信它涉及到试图挽救后再次你只是删除了它的一个实例的表单集。当super的呼叫被移除时,代码工作得很好。

长和短,有没有什么办法可以让我缺少定制保存行为允许子类自己重写?

+2

刚刚发现[一个未解票(https://code.djangoproject.com/门票/ 17988) – okm 2012-04-30 10:50:58

回答

5

这是一个doozie。

我有一些有趣的闲逛,看来所有的行动发生在这里django.forms.models.BaseModelFormSet

的问题是不管ModelFormSet.save()删除的commit标志的情况下,没有修改的形式来反映被删除的状态。

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

​​

我能解决这个问题的唯一方法是修补BaseModelFormset.save_existing_objects如果一个对象被删除从self.forms删除形式。

做了一些测试,并没有出现任何不良影响。

+0

感谢您的详细分析。我只是想要一个完整的检查来确保我不是完全错过了一些东西,但是如果Django源需要补丁,那么这看起来像是一个错误报告的主要候选者。 – 2012-04-30 16:46:24

0

@克里斯普拉特帮助:

我上涨了调用超出于习惯比什么都重要的, 不认为它实际上将导致表单集保存两次。

我试图进一步覆盖save_formset为了发送后保存信号。我只是不明白,调用super()只是第二次保存formset。

为了应对super()问题,我创建了一个save_formset_now方法与我的自定义代码,我打电话的时候我通过admin.ModelAdmin孩子覆盖save_formset

这是代码,这似乎也照顾删除问题,使用Django 1.10在2016年

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