保存模型前验证内联数据

7 投票
1 回答
1668 浏览
提问于 2025-04-17 12:42

假设我有这两个模型:

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中,所有对象在保存它们的外键或多对多相关对象之前都会先被保存。这并不是一个缺陷,而是有原因的:因为相关的对象在保存之前,所关联的对象还没有定义 idid 在对象第一次保存到数据库之前是 None)。

我相信我不是第一个遇到这个问题的人。那么,有没有办法实现我想做的事情呢?我在想,也许可以通过 TabularInlineModelAdmin 来做一些管理上的小技巧……?

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

撰写回答