通过Django创建数据库约束

gue*_*tli 9 django postgresql check-constraints

我有一个看起来像这样的Django模型:

class Dummy(models.Model):
    ...
    system = models.CharField(max_length=16)
Run Code Online (Sandbox Code Playgroud)

我希望system永远不会是空的或包含空格.

我知道如何在Django中使用验证器.

但我会在数据库级别强制执行此操作.

为此创建数据库约束的最简单和类似django的方法是什么?

我使用PostgreSQL,不需要支持任何其他数据库.

Ral*_*alf 12

第一个问题:通过Django创建数据库约束

似乎django还没有这种能力.有一张9年历史的门票,但是我不会屏住这长久以来的事情.

您可以查看包django-db-constraints,通过它可以在模型中定义约束Meta.我没有测试这个软件包,所以我不知道它真的有用.

# example using this package
class Meta:
    db_constraints = {
        'price_above_zero': 'check (price > 0)',
    }
Run Code Online (Sandbox Code Playgroud)

第二个问题:字段system永远不应为空,也不应包含空格

现在我们需要checkpostgres语法中构建约束来实现它.我想出了这些选择:

  1. system删除空格后检查长度是否不同.使用此答案中的想法,您可以尝试:

    /* this check should only pass if `system` contains no
     * whitespaces (`\s` also detects new lines)
     */
    check ( length(system) = length(regexp_replace(system, '\s', '', 'g')) )
    
    Run Code Online (Sandbox Code Playgroud)
  2. 检查空白数是否为0.为此我们可以regexp_matches:

    /* this check should only pass if `system` contains no
     * whitespaces (`\s` also detects new lines)
     */
    check ( length(regexp_matches(system, '\s', 'g')) = 0 )
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,该length函数不能使用,regexp_matches因为后者返回一set of text[]组(数组),但我找不到正确的函数来计算该集合的元素.


最后,将它们整合在一起,您的方法可能如下所示:

class Dummy(models.Model):
    # this already sets NOT NULL to the field in the database
    system = models.CharField(max_length=16)

    class Meta:
        db_constraints = {
            'system_no_spaces': 'check ( length(system) > 0 AND length(system) = length(regexp_replace(system, "\s", "", "g")) )',
        }
Run Code Online (Sandbox Code Playgroud)

这将检查字段值:

  1. 不包含NULL(默认情况下CharField添加NOT NULL约束)
  2. 不为空(第一部分check:length(system) > 0)
  3. 没有空格(check替换空格后的第二部分:相同的长度)

让我知道这对您有何影响,或者这种方法是否有问题或缺点.

  • 该票证现已修复,更改很有可能在Django 2.2中可用。 (3认同)
  • 在 2.2 中确认可用性:https://docs.djangoproject.com/en/dev/ref/models/constraints/ (2认同)

Ces*_*ssa 11

2019更新

Django 2.2添加了对数据库级约束的支持。新的CheckConstraintUniqueConstraint类可添加自定义数据库约束。使用Meta.constraints选项将约束添加到模型中。

您的系统验证如下所示:

class Dummy(models.Model):
    ...
    system = models.CharField(max_length=16)

    class Meta:
        constraints = [
            CheckConstraint(
                check=~Q(system="") & ~Q(system__contains=" "),
                name="system_not_blank")
        ]
Run Code Online (Sandbox Code Playgroud)


ndp*_*dpu 7

您可以CHECK通过自定义django迁移添加约束.要检查字符串长度,您可以使用char_length函数并position检查包含的空格.

来自postgres docs的引用(https://www.postgresql.org/docs/current/static/ddl-constraints.html):

检查约束是最通用的约束类型.它允许您指定某列中的值必须满足布尔(真值)表达式.

RunSQL可以使用在迁移操作中运行任意sql (https://docs.djangoproject.com/en/2.0/ref/migration-operations/#runsql):

允许在数据库上运行任意SQL - 对于Django不直接支持的数据库后端的更高级功能(如部分索引)非常有用.

创建空迁移:

python manage.py makemigrations --empty yourappname
Run Code Online (Sandbox Code Playgroud)

添加sql以创建约束:

# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
         migrations.RunSQL('ALTER TABLE appname_dummy ADD CONSTRAINT syslen '
                           'CHECK (char_length(trim(system)) > 1);',
                           'ALTER TABLE appname_dummy DROP CONSTRAINT syslen;'),
         migrations.RunSQL('ALTER TABLE appname_dummy ADD CONSTRAINT syswh '
                           'CHECK (position(' ' in trim(system)) = 0);',
                           'ALTER TABLE appname_dummy DROP CONSTRAINT syswh;')


    ]
Run Code Online (Sandbox Code Playgroud)

运行迁移:

python manage.py migrate yourappname
Run Code Online (Sandbox Code Playgroud)