保存模型前验证内联数据
假设我有这两个模型:
class Distribution(models.Model):
name = models.CharField(max_length=32)
class Component(models.Model):
distribution = models.ForeignKey(Distribution)
percentage = models.IntegerField()
我在使用一个简单的 TabularInline
来在 Distribution
的管理表单中显示 Component
:
class ComponentInline(admin.TabularInline):
model = Component
extra = 1
class DistributionAdmin(admin.ModelAdmin):
inlines = [ComponentInline]
我的目标是在保存之前,验证所有 Component
的百分比加起来是否等于100。听起来很简单,所以我这样做了:
# ... Inside the Distribution model
def clean(self):
# Sum of components must be 100
total_sum = sum(comp.percentage for comp in self.component_set.all())
if total_sum != 100:
raise ValidationError('Sum of components must be 100%')
但这根本行不通,因为在Django中,所有对象在保存它们的外键或多对多相关对象之前都会先被保存。这并不是一个缺陷,而是有原因的:因为相关的对象在保存之前,所关联的对象还没有定义 id
(id
在对象第一次保存到数据库之前是 None
)。
我相信我不是第一个遇到这个问题的人。那么,有没有办法实现我想做的事情呢?我在想,也许可以通过 TabularInline
或 ModelAdmin
来做一些管理上的小技巧……?
1 个回答
5
这里有一个(未经测试的)想法,如果你愿意把验证从模型转移到内联表单集的话:
你可以创建一个新的类,继承自 BaseInlineFormSet
,然后重写它的清理方法,来检查百分比的总和。
from django.forms.models import BaseInlineFormSet
from django.core.exceptions import ValidationError
class ComponentInlineFormSet(BaseInlineFormSet):
def clean(self):
"""Check that sum of components is 100%"""
if any(self.errors):
# Don't bother validating the formset unless each form is valid on its own
return
total_sum = sum(form.cleaned_data['percentage'] for form in self.forms)
if total_sum != 100:
raise ValidationError('Sum of components must be 100%')
接着在 ComponentInline
中使用你刚刚创建的内联表单集。
class ComponentInline(admin.TabularInline):
model = Component
extra = 1
formset = ComponentInlineFormSet