Django:覆盖save()方法:如何调用子类的delete()方法

时间:2010-03-05 04:43:25

标签: django django-models django-admin

设置=

我有这堂课,成绩单:

class Transcript(models.Model):    
    body = models.TextField('Body')
    doPagination = models.BooleanField('Paginate')
    numPages = models.PositiveIntegerField('Number of Pages')

和这个类,TranscriptPages(models.Model):

class TranscriptPages(models.Model):
    transcript = models.ForeignKey(Transcript)
    order = models.PositiveIntegerField('Order')
    content = models.TextField('Page Content', null=True, blank=True)

我正在尝试创建的管理员行为是让用户使用长文档的全部内容填充Transcript.body,如果他们设置Transcript.doPagination = True并保存Transcript管理员,我将自动拆分身体成n个成绩单页。

在管理员中,TranscriptPages是Transcript Admin的StackedInline。

为了做到这一点,我覆盖了Transcript的保存方法:

def save(self):
    if self.doPagination:
        #do stuff
        super(Transcript, self).save()
    else:
        super(Transcript, self).save()

问题=

当Transcript.doPagination为True时,我想手动删除所有引用此Transcript的TranscriptPages,以便我可以从头开始再次创建它们。

所以,我认为这样可行:

#do stuff   
TranscriptPages.objects.filter(transcript__id=self.id).delete()
super(Transcript, self).save()

但是当我尝试时我得到了这个错误:

  

异常类型:ValidationError   例外值:[你选择一个有效的   选择。那个选择不是其中之一   可供选择。']

...这是在引发异常之前堆栈跟踪中的最后一件事:

  save_existing_objects中的

... / django / forms / models.py

     
      
  1. pk_value = form.fields [pk_name] .clean(raw_pk_value)
  2.   

其他修复尝试:

  • t = self.transcriptpages_set.all()。删除() (其中self = Transcript from the save()方法)
  • 循环t(上方)并单独删除每个项目
  • 在TranscriptPages上调用delete方法
  • 发出post_save信号

有什么想法吗?管理员如何做到这一点?

更新:每次偶尔因为我正在玩代码我可以得到一个不同的错误(下面),但是它只是消失了而我再也无法复制它......直到下一个随机时间。

  

例外类型:
  MultiValueDictKeyError异常   值:“Key'transcriptpages_set-0-id'   没找到“   例外地点:     ... / django / utils / datastructures.py in    getitem ,第203行

以及跟踪的最后一行:

  _construct_form

中的

... / django / forms / models.py.      

      
  1. form = super(BaseInlineFormSet,self)._ construct_form(i,** kwargs)
  2.          getitem 中的

    ... / django / utils / datastructures.py

         
        
    1. pk = self.data [pk_key]
    2.   

3 个答案:

答案 0 :(得分:1)

最后这是一个时间问题。当从多个中删除仅一个子对象时,没有问题。如果我一次删除太多子对象,则可能发生错误,因为删除操作试图引用不在的ID。这就是为什么没有工作,不是信号,而不是[对象] _set。我通过使用jquery在子对象的编辑表单中设置隐藏变量来修复它,这导致对象首先处理更新(减慢它的处理速度)然后删除。

答案 1 :(得分:0)

您可能在创建之前尝试访问Transaction.id。此外,您可以尝试通过transactionpage_set通过Transaction对象访问TransactionPage对象(请参阅query docs about FOO_set notation)。

def save(self):
    super(Transcript, self).save()
    if self.doPagination:
        self.transaction_set.all().delete() # .all() may be optional
        pages = ... # whatever you do to split the content
        self.numPages = len(pages)
        self.save() # yes, a second save if you store numPages in Transaction
        for page_number in range(self.numPages):
            TransactionPage.objects.create(content=pages[page_number], 
                                           order=page_number, transaction=self)

您也可以切换到不在事务中存储numPages,而是通过属性访问它。

class Transaction(models.Model):
    # ...
    # replace numPages with
    @property
    def page_count(self):
        return self.transactionpage_set.count() or 1

然后,如果你更进一步,你总是可以使用TransactionPage对象进行显示。这将允许您在上面的save()方法中删除额外的self.save()调用。它还允许您通过始终显示TransactionPage.content而不是有条件地显示Transaction.body(如果您正在分页)和TransactionPage.content来简化模板。

class Transaction(models.Model):
    body = models.TextField()
    paginate = models.BooleanField()

    @property
    def page_count(self):
        return self.transactionpage_set.count() # no more "or 1" cruft!

    def save(self):
        super(Transcript, self).save()
        self.transaction_set.delete() # might need an .all() before .delete()
        if self.paginate:
            # Do whatever you do to split the body into pages.
            pages = ... 
        else:
            # The only page is the entire body.
            pages = [self.body]
        for (page_number, page_content) in enumerate(pages):
            TransactionPage.objects.create(content=page_content, 
                                           order=page_number, transaction=self)

答案 2 :(得分:0)

另一种可能的解决方案可能是覆盖ModelAdmin中的save_formset()以完全阻止更新:

def save_formset(self, request, form, formset, change):
     if (form.cleaned_data['do_pagination'] and 
            formset.model == TranscriptPages):
        formset.changed_objects = []
        formset.new_objects = []
        formset.deleted_objects = []
    else:
        formset.save()

然后,你可以在Modal.save()中做任何你喜欢的事情。请注意,如果想要更多地挖掘内部构件,可能会有一种更优雅/更加突破的方法来阻止formset处理。