Validate inlines before saving model

被刻印的时光 ゝ 提交于 2019-12-23 09:32:03

问题


Let's say I have these two models:

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

class Component(models.Model):
    distribution = models.ForeignKey(Distribution)
    percentage = models.IntegerField()

And I'm using a simple TabularInline to show Components inside the Distribution admin form:

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

class DistributionAdmin(admin.ModelAdmin):
    inlines = [ComponentInline]

So, my goal is to validate if the percentages of all the Components of the Distribution sum 100 before saving it. Sounds simple, so I did:

# ... 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%')

But this will never work work, because in Django all objects are saved before saving its foreign-key or many2many related objects, this is not a flaw, it has a reason: it cannot save the related objects first, because the object to which they are related doesn't have an id defined yet (id is None until the object is saved for the first time in the DB).

I'm sure I'm not the first guy to run into this issue. So, is there a way to accomplish what I'm trying to do? I was thinking maybe a admin hack using TabularInline or ModelAdmin ... ?


回答1:


Here's an (untested) idea, if you're happy to move the validation from the model to the inline formset:

Subclass BaseInlineFormSet and override the clean method to check the sum of the percentages.

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%')

Then use your inline formset in the ComponentInline.

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


来源:https://stackoverflow.com/questions/9215747/validate-inlines-before-saving-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!