通过计算多行的总和来验证Django模型数据

时间:2014-04-11 19:58:55

标签: django

我有两种模式:

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


class Referral(models.Model):
    referring_user = models.ForeignKey(User, related_name="referrals")
    referred_user  = models.ForeignKey(User, related_name="referrers")
    percentage     = models.PositiveIntegerField()

这个想法是每个用户都有n个推荐人,并且应该至少有一个。每个引荐来源都有一个percentage,当添加到其他引荐来源时,它应该加起来为100%

所以用户" Alice"可能有推荐人" Bob" (50%)和" Cynthia" (50%),用户"唐纳德"可能有一个推荐人:" Erin" (100%)。

我遇到的问题是验证。有没有办法(最好是使用admin.TabularInline与Django管理员合作的方式)我可以通过验证拒绝保存User如果Refferrals != 100%的总和?

理想情况下,我希望这种情况发生在表单/管理员级别,而不是覆盖User.save(),但此时我不知道从哪里开始。大多数Django的验证代码似乎是原子的,并且我之前在Django中完成了多行验证。

2 个答案:

答案 0 :(得分:1)

作为per the Django docsclean()是为您的目的实施的官方功能。你可以想象一个看起来像这样的函数:

from django.core.exceptions import ValidationError

def clean(self):
    total_percentage = 0
    for referrer in self.referrers.all():
        total_percentage += referrer.percentage
    if total_percentage !== 100:
        raise ValidationError("Referrer percentage does not equal 100")

答案 1 :(得分:1)

在Jerry Meng建议我查看data属性而不是cleaned_data后,我开始探究admin.ModelAdmin以了解我如何访问该方法。我发现get_form似乎返回了一个表单类,所以我重写了该方法以捕获返回的类,将其子类化,并覆盖那里的.clean()

进入内部后,我循环使用正则表达式self.data,找到相关字段然后进行数学计算。

import re
from django import forms
from django.contrib import admin

class UserAdmin(admin.ModelAdmin):

    # ...

    def get_form(self, request, obj=None, **kwargs):

        parent = admin.ModelAdmin.get_form(self, request, obj=None, **kwargs)

        class PercentageSummingForm(parent):
            def clean(self):

                cleaned_data = parent.clean(self)

                total_percentage = 0
                regex = re.compile(r"^referrers-(\d+)-percentage$")

                for k, v in self.data.items():
                    match = re.match(regex, k)
                    if match:
                        try:
                            total_percentage += int(v)
                        except ValueError:
                            raise forms.ValidationError(
                                "Percentage values must be integers"
                            )

                if not total_percentage == 100:
                    raise forms.ValidationError(
                        "Percentage values must add up to 100"
                    )

                return cleaned_data

        return PercentageSummingForm