У меня есть модель Django, которая выглядит так:
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
Я хочу, чтобы system никогда не был пустым или содержал пробелы.
Я знаю, как использовать валидаторы в Django.
Но я бы обеспечил это на уровне базы данных.
Каков самый простой и похожий на django способ создать для этого ограничение БД?
Я использую PostgreSQL, и мне не нужно поддерживать какую-либо другую базу данных.





Я изменяю свой ответ, чтобы учесть ваши требования.
Итак, если вы хотите запустить ограничение БД, попробуйте следующее:
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()
Добро пожаловать, @guettli. Приношу свои извинения, я не читал Я знаю, как использовать валидаторы в Django.
@guettli, я просто изменяю свой ответ
зачем создавать соединение с БД? В моем коде используется Django, и я хотел бы использовать способ django для доступа к соединению / курсору БД. Я не вижу, чтобы вы создавали ограничение БД. Вы выполняете SQL в процедуре проверки. Для меня это другое дело.
Первая проблема: создание ограничения базы данных через Django
А)
Похоже, что в django еще нет встроенной возможности. Для этого есть открытый проездной билет 9-летней давности, но я бы не стал задерживать дыхание из-за того, что творится так долго.
Обновлено: Начиная с версии 2.2 (апрель 2019 г.), Django поддерживает проверить ограничения уровня базы данных.
Б) Вы можете заглянуть в пакет django-db-ограничения, с помощью которого вы можете определить ограничения в модели Meta. Я не тестировал этот пакет, поэтому не знаю, насколько он действительно полезен.
# example using this package
class Meta:
db_constraints = {
'price_above_zero': 'check (price > 0)',
}
Поле Вторая проблема:system никогда не должно быть пустым или содержать пробелы
Теперь нам нужно построить ограничение check в синтаксисе postgres, чтобы добиться этого. Я придумал такие варианты:
Проверьте, отличается ли длина 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')) )
Убедитесь, что количество пробелов равно 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: та же длина после замены пробелов)Сообщите мне, как это работает для вас, и если у этого подхода есть проблемы или недостатки.
Да, ваше решение с приложением django-db-constraints работает. Это намного лучше, чем писать миграции вручную с помощью migrations.RunSQL (...).
Теперь этот билет исправлен, и изменения, скорее всего, будут доступны в Django 2.2.
Подтвержденная доступность в 2.2: docs.djangoproject.com/en/dev/ref/models/constraints
Вы можете добавить ограничение CHECK с помощью пользовательской миграции django. Чтобы проверить длину строки, вы можете использовать функцию char_length и position для проверки наличия пробелов.
Цитата из документов postgres (https://www.postgresql.org/docs/current/static/ddl-constraints.html):
A check constraint is the most generic constraint type. It allows you to specify that the value in a certain column must satisfy a Boolean (truth-value) expression.
Для запуска произвольного sql в миграции можно использовать операцию RunSQL (https://docs.djangoproject.com/en/2.0/ref/migration-operations/#runsql):
Allows running of arbitrary SQL on the database - useful for more advanced features of database backends that Django doesn’t support directly, like partial indexes.
Создать пустую миграцию:
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
В Django 2.2 добавлена поддержка ограничения на уровне базы данных. Новые классы CheckConstraint и Уникальное ограничение позволяют добавлять собственные ограничения базы данных. Ограничения добавляются к моделям с помощью Параметр Meta.constraints.
Проверка вашей системы будет выглядеть примерно так:
from django.db import models
from django.db.models.constraints import CheckConstraint
from django.db.models.query_utils import Q
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
class Meta:
constraints = [
CheckConstraint(
check=~Q(system = "") & ~Q(system__contains = " "),
name = "system_not_blank")
]
Этот ответ должен быть ИДЕАЛЬНЫМ решением, поскольку он использует встроенную функцию Django для достижения желаемой цели.
Спасибо за попытку помочь. Ваше решение выполняет проверку внутри Django. Мне нужна проверка в базе данных.