根据另一个字段的值在Django模型上添加约束

时间:2019-06-26 08:53:39

标签: python django database django-models

我有一个具有1个主键和3个字段(简化)的简单模型:

  • 及格分数
  • 最高分数
  • 最大尝试次数

此模型是通过从django.db.models继承而创建的。这是最小的可复制代码:

from django.db import models
class QuestionSet(models.Model):
    passingscore = models.PositiveSmallIntegerField("passing score")
    maxscore = models.PositiveSmallIntegerField("max score")
    maxattempt = models.PositiveSmallIntegerField("max attempt")

我想添加一个约束,以使数据库中的passingscore永远不要大于maxscore

根据this thread on StackOverflow,我使用了跨越多个字段的约束,例如unique_together。但是显然这是一个不同的用例。

我还简要地考虑过直接添加PostgreSQL代码来添加约束:

ALTER TABLE tableB ADD CONSTRAINT score_policy_1 CHECK (maxscore >= passingscore) 

但是,这违反了使用ORM的目的,并且违反了“松散耦合”的哲学,这使得在不同数据库后端之间迁移变得困难。

如果有可能,请指出一种在Django中编写此约束的更惯用的方式。

2 个答案:

答案 0 :(得分:2)

这应该适合您的情况。

from django.db import models
class QuestionSet(models.Model):
    passingscore = models.PositiveSmallIntegerField("passing score")
    maxscore = models.PositiveSmallIntegerField("max score")
    maxattempt = models.PositiveSmallIntegerField("max attempt")
    
    class Meta:
        constraints = [
            models.CheckConstraint(
                name="%(app_label)s_%(class)s_passingscore_lte_maxscore",
                check=models.Q(passingscore__lte=models.F("maxscore")),
            )
        ]

答案 1 :(得分:1)

您可以在视图层或数据库层中进行操作。

如果要在数据库层中执行此操作,则可以覆盖模型的save方法,并在其中检查值:

def save(self, *args, **kwargs):

    if self.passingscore > self.maxscore:
        raise ValueError("passingscore can't be greater than maxscore")

    super().save(*args, **kwargs)

如果要处理数据库约束,则可以通过将BaseConstraint子类化来创建自己的约束类,并将sql放在constraint_sql方法中。