Создать ограничение БД через Django

У меня есть модель Django, которая выглядит так:

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

Я хочу, чтобы system никогда не был пустым или содержал пробелы.

Я знаю, как использовать валидаторы в Django.

Но я бы обеспечил это на уровне базы данных.

Каков самый простой и похожий на django способ создать для этого ограничение БД?

Я использую PostgreSQL, и мне не нужно поддерживать какую-либо другую базу данных.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
15
0
8 010
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я изменяю свой ответ, чтобы учесть ваши требования.

Итак, если вы хотите запустить ограничение БД, попробуйте следующее:

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()

Спасибо за попытку помочь. Ваше решение выполняет проверку внутри Django. Мне нужна проверка в базе данных.

guettli 20.04.2018 11:14

Добро пожаловать, @guettli. Приношу свои извинения, я не читал Я знаю, как использовать валидаторы в Django.

Panos Angelopoulos 20.04.2018 11:15

@guettli, я просто изменяю свой ответ

Panos Angelopoulos 23.04.2018 14:04

зачем создавать соединение с БД? В моем коде используется Django, и я хотел бы использовать способ django для доступа к соединению / курсору БД. Я не вижу, чтобы вы создавали ограничение БД. Вы выполняете SQL в процедуре проверки. Для меня это другое дело.

guettli 23.04.2018 14:28
Ответ принят как подходящий

Первая проблема: создание ограничения базы данных через 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, чтобы добиться этого. Я придумал такие варианты:

  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')) )
    
  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 )
    

    Обратите внимание, что функция 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")) )',
        }

Это проверяет, что значение полей:

  1. не содержит NULL (CharField по умолчанию добавляет ограничение NOT NULL)
  2. не пусто (первая часть check: length(system) > 0)
  3. не имеет пробелов (вторая часть check: та же длина после замены пробелов)

Сообщите мне, как это работает для вас, и если у этого подхода есть проблемы или недостатки.

Да, ваше решение с приложением django-db-constraints работает. Это намного лучше, чем писать миграции вручную с помощью migrations.RunSQL (...).

guettli 27.04.2018 10:07

Теперь этот билет исправлен, и изменения, скорее всего, будут доступны в Django 2.2.

Eugene Pakhomov 09.11.2018 12:59

Подтвержденная доступность в 2.2: docs.djangoproject.com/en/dev/ref/models/constraints

coler-j 11.03.2019 17:16

Вы можете добавить ограничение 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

Обновление 2019

В 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 для достижения желаемой цели.

lbris 11.12.2020 09:48

Другие вопросы по теме