保存模型前验证内联

jul*_*ria 6 python django foreign-keys

假设我有这两个模型:

class Distribution(models.Model):
    name = models.CharField(max_length=32)

class Component(models.Model):
    distribution = models.ForeignKey(Distribution)
    percentage = models.IntegerField()
Run Code Online (Sandbox Code Playgroud)

我正在使用一个简单的在管理表单中TabularInline显示Components Distribution:

class ComponentInline(admin.TabularInline):
    model = Component
    extra = 1

class DistributionAdmin(admin.ModelAdmin):
    inlines = [ComponentInline]
Run Code Online (Sandbox Code Playgroud)

因此,我的目标是在保存之前验证总和100 的所有Components 的百分比Distribution.听起来很简单,所以我做了:

# ... 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%')
Run Code Online (Sandbox Code Playgroud)

但是这永远不会起作用,因为在Django中所有对象都在保存其外键或许多相关对象之前保存,这不是一个缺陷,它有一个原因:它不能先保存相关对象,因为它们对象是有关没有一个id定义,但(idNone直到对象保存在数据库第一次).

我敢肯定我不是第一个遇到这个问题的人.那么,有没有办法完成我想要做的事情?我想也许是管理员黑客使用TabularInlineModelAdmin......?

Ala*_*air 5

如果您愿意将验证从模型移动到内联表单集,那么这是一个(未经测试的)想法:

子类化BaseInlineFormSet并重写 clean 方法以检查百分比总和。

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%')
Run Code Online (Sandbox Code Playgroud)

然后在ComponentInline.

class ComponentInline(admin.TabularInline):
    model = Component
    extra = 1
    formset = ComponentInlineFormSet
Run Code Online (Sandbox Code Playgroud)