django 自定义字段「sql唯一值约束」

互联网 2023-03-03 19:27:08

今天给大家普及一下django 自定义字段「sql唯一值约束」相关知识,最近很多在问django 自定义字段「sql唯一值约束」,希望能帮助到您。

我之前介绍过使用Django的CheckConstraint类来验证带有choices的字段和总和为100%的百分比字段。这里是另一个用例。

我最近在做的一个项目要存储“分数”,它可能只有一个单个的输入值:一个整数、一个小数或一个时间段(duration)。因为它们具有不同的类型,所以它们被存储在数据库中的不同列中。因此,对于单个分数,只能填充这些列中的一列(不为空)。

你可以通过使用模型继承来解决这个问题。这样做的缺点是,无论是使用具体继承还是抽象继承,你都会需要多个表。对于单个不同的字段来说,这也是大量的工作。

我们没有这样做,而是采用了具有多个值列的单一模型方法,我们只需要设置其中一个值列。这个模型看起来像这样:

(IntegerChoices是Django 3.0新枚举类型的一种)

如果type是 ScoreType.POINTS,则value_points列应该被设置。同样,如果type是ScoreType.DURATION,则value_duration列应该被设置。

尽管你对我们的Python代码满足这个约束充满自信,除非你让数据库强制执行它,否则一个错误就可能打破这个假设。例如,你可能不小心写了这样一个查询:

(将 datetime导入为dt.)

如此糟糕的数据可能会产生各种意想不到的后果。如果你发现这样的糟糕数据已经被创建了一段时间,它可能需要很长时间才能解开。如果可以的话,最好尽早预防!

为此,你可以添加一个CheckConstraint来强制保证填充值列与type相匹配。它看起来是这样的:

约束是使用Q对象定义的,Q对象会接受与filter相同的参数来限制这些情况。在这里,我们实际上列出了这两种有效的情况,并使用Python的按位或运算符|对它们进行或运算。由于Python中的限制,Q不能对此使用普通的or运算符。

在makemigrations并应用此迁移之后,你可以测试该约束。你可以创建刚好合法的Score实例:

但是如果你尝试保存错误的数据, 你将得到一个 IntegrityError:

运行的很好。

你还可以将该约束与代理模型结合使用,以根据类型划分Score。我还没有完全尝试这一点,但是它看起来像多表继承的一个可行的替代方法。你甚至可以添加一个助手函数来自动生成类。

你可以像这样完成一个没有任何自动生成的简单的“Points”实现:

DURATION实现只需要一点复制-粘贴-替换即可。

这个实现使用一个自定义管理器来只返回正确类型的实例。并且它还覆盖了__init__ 来强制保证type。我确信我们还可以添加一些其他的方法来使其更加流畅,但是你已经了解了其中的思想。

结语

有关本文中使用的示例项目的源代码,包括该检查约束的一个额外的自动生成工具,请在GitHub上查看。(地址:https://github.com/adamchainz/django-demo-constraint-single-column-not-/blob/master/example/core/models.py )

希望这篇文章能帮助你提升你的CheckConstraint应用,

—Adam

英文原文:https://adamj.eu/tech/2020/03/25/django-check-constraints-one-field-set/ 译者:浣熊君( ・᷄৺・᷅ )