我想使用Django的CheckConstraint
添加check
约束到使用postgresql的num_nonnulls()
函数的表,类似于:
create table foo(
a text,
b int,
[...],
check num_nonnulls(a, b, ...) = n);
n
是一个常数,但对于不同的表可能不同(主要是1
,但我想找到一个通用的解决方案)。这是我得到的结果:
class Foo(models.Model):
a = models.TextField(null=True)
b = models.IntegerField(null=True)
[...]
class Meta:
constraints = [
models.CheckConstraint(
check=models.ExpressionWrapper(
models.Func('a', 'b', function='num_nonnulls'),
models.BooleanField()),
name='num_nonnulls_check')]
这当然错过了将num_nonnulls()
的结果与某个常数整数进行比较的步骤。我尝试定义一个函数来做这个:
def equals(a, b):
return models.Func(a, b, template='%(expressions[0])s = %(expressions[1])s')
但这不起作用,因为模板参数expressions
是(我认为)一个字符串(和%
-模板字符串没有这种语法来提取参数的部分,我认为)。
我该往哪里走?
我想找到一个解决方案,允许我使用Django ORM支持的任意表达式,并使用相等或不相等关系(例如=
或<=
)将这些表达式与其他表达式或常量进行比较。
从Django 4开始,这是可能的,参见下面的变更日志条目:
查找表达式现在可以在QuerySet注释、聚合中使用,也可以直接在过滤器中使用。
这应该适合你的例子。
from django.db import models
from django.db.models.constraints import CheckConstraint
from django.db.models.expressions import Func
from django.db.models import Value
from django.db.models.fields import IntegerField, TextField
class Foo(models.Model):
a = models.TextField(null=True)
b = models.IntegerField(null=True)
class Meta:
constraints = [
CheckConstraint(
name='num_nonnulls_check',
check=Exact(
lhs=Func('a', 'b', function='num_nonnulls', output_field=IntegerField()),
rhs=Value(1),
),
)
]