将表达式与 CHECK 约束中的常量进行比较

Compare expression with constant in CHECK constraint

我想使用 Django 的 CheckConstraintcheck 约束添加到 table 使用 PostgreSQLs num_nonnulls() 函数,类似于:

create table foo(
    a text,
    b int,
    [...],
    check num_nonnulls(a, b, ...) = n);

n 是一个常量,但对于不同的 table 可能会有所不同(主要是 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 开始,这是可能的,请参阅此更改日志条目:

Lookup expressions may now be used in QuerySet annotations, aggregations, and directly in filters.

这应该符合您的示例。

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),
                ),
            )
        ]