我有一个看起来像这样的Django模型:
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
我希望system
永远不会为空或包含空格。
我知道如何在Django中使用验证器。
但我会在数据库级别强制执行此操作。
为此创建数据库约束的最简单和类似django的方法是什么?
我使用PostgreSQL,不需要支持任何其他数据库。
答案 0 :(得分:8)
第一期:通过Django
创建数据库约束似乎django还没有这种能力。有一个9岁的开放ticket为它,但我不会屏住呼吸这个长期的事情。
您可以查看包django-db-constraints,通过该包可以在模型Meta
中定义约束。我没有测试这个软件包,所以我不知道它真的有用。
# example using this package
class Meta:
db_constraints = {
'price_above_zero': 'check (price > 0)',
}
第二个问题:字段system
永远不应为空,也不应包含空格
现在我们需要在check
语法中构建postgres
约束来实现这一目标。我提出了这些选择:
删除空格后,检查system
的长度是否不同。使用this answer中的提示,您可以尝试:
# this check should only pass if `system` contains no
# whitespaces (`\s` also detects new lines)
check ( length(system) = length(regexp_replace(system, '\s', '', 'g')) )
检查空格数是否为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 )
请注意,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")) )',
}
这会检查字段值是否为
CharField
添加NOT NULL
约束)check
的第一部分:length(system) > 0
)check
的第二部分:替换空格后的长度相同)让我知道这对你有用,或者这种方法有问题或缺点。
编辑:从2.2版开始,Django拥有数据库级CheckConstraint
答案 1 :(得分:6)
您可以通过自定义django迁移添加CHECK
约束。要检查字符串长度,您可以使用char_length
函数和position
来检查是否包含空格。
来自postgres docs(https://www.postgresql.org/docs/current/static/ddl-constraints.html)的引用:
检查约束是最通用的约束类型。它允许你 指定某列中的值必须满足布尔值 (真值)表达。
在迁移中运行任意sql RunSQL
可以使用https://docs.djangoproject.com/en/2.0/ref/migration-operations/#runsql}操作:
允许在数据库上运行任意SQL - 对更多内容非常有用 Django不支持的数据库后端的高级功能 直接,就像部分索引一样。
创建空迁移:
python manage.py makemigrations --empty yourappname
添加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;')
]
运行迁移:
python manage.py migrate yourappname
答案 2 :(得分:1)
我修改答案以达到您的要求。
因此,如果您想运行数据库约束,请尝试以下方法:
import psycopg2
def your_validator():
conn = psycopg2.connect("dbname=YOURDB user=YOURUSER")
cursor = conn.cursor()
query_result = cursor.execute("YOUR QUERY")
if query_result is Null:
# Do stuff
else:
# Other Stuff
然后使用pre_save
信号。
在models.py
文件中添加
from django.db.models.signals import pre_save
class Dummy(models.Model):
...
@staticmethod
def pre_save(sender, instance, *args, **kwargs)
# Of course, feel free to parse args in your def.
your_validator()
答案 3 :(得分:1)
Django 2.2添加了对database-level constrains的支持。新的CheckConstraint和UniqueConstraint类可添加自定义数据库约束。使用Meta.constraints option将约束添加到模型中。
您的系统验证看起来像这样:
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
class Meta:
constraints = [
CheckConstraint(
check=~Q(system="") & ~Q(system__contains=" "),
name="system_not_blank")
]