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

Dan*_*inn 7 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()
Run Code Online (Sandbox Code Playgroud)

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

因此,用户"Alice"可能有推荐人"Bob"(50%)和"Cynthia"(50%),而用户"Donald"可能有一个推荐人:"Erin"(100%).

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

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

Dan*_*inn 2

在杰里·孟建议我查看该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
Run Code Online (Sandbox Code Playgroud)