设置=
我有这堂课,成绩单:
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
- pk_value = form.fields [pk_name] .clean(raw_pk_value)
醇>
其他修复尝试:
有什么想法吗?管理员如何做到这一点?
更新:每次偶尔因为我正在玩代码我可以得到一个不同的错误(下面),但是它只是消失了而我再也无法复制它......直到下一个随机时间。
例外类型:
MultiValueDictKeyError异常 值:“Key'transcriptpages_set-0-id' 没找到“ 例外地点: ... / django / utils / datastructures.py in getitem ,第203行
以及跟踪的最后一行:
_construct_form 中的... / django / forms / models.py.
- form = super(BaseInlineFormSet,self)._ construct_form(i,** kwargs)
醇> getitem 中的... / django / utils / datastructures.py
- pk = self.data [pk_key]
醇>
答案 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处理。