如何在django模型中设置表格约束“deferredrable initial deferred”?

时间:2016-11-30 15:01:14

标签: django postgresql django-models constraints

我正在尝试使用postgresql数据库为django中的表模型设置约束。

我可以通过postgresql用这句话来做:

ALTER TABLE public.mytable ADD CONSTRAINT "myconstraint" UNIQUE(field1, field2) DEFERRABLE INITIALLY DEFERRED;

但我想通过django模型来做。 阅读django官方文档我没有发现任何相关内容。

我需要这样的东西:

class Meta:
  unique_together = (('field1', 'field2',), DEFERRABLE INITIALLY DEFERRED)

是否可以做这样的事情?

3 个答案:

答案 0 :(得分:2)

Django不支持。

您可以使用自定义SQL执行此操作。在models.py中添加以下内容:

from django.db import connection
from django.db.models.signals import post_migrate

def after_migrate(sender, **kwargs):
    cursor = connection.cursor()
    cursor.execute('ALTER TABLE public.mytable ALTER CONSTRAINT '
                   'myconstraint DEFERRABLE INITIALLY DEFERRED')

post_migrate.connect(after_migrate)

虽然我过去做过这样的事情,但我发现多年来我更喜欢让我的工作更简单,更独立于任何特定的RDBMS。例如,您确实希望支持SQLite,因为它使开发变得更加容易。设计稍有变化,你可以经常摆脱这些东西。

答案 1 :(得分:1)

我将通过一次迁移来做到这一点。首先以编程方式获取唯一的约束名称,然后删除并重新添加(因为更改它似乎仅适用于FK约束,而不适用于唯一约束)。添加反向迁移也可以消除这种情况。

from django.db import migrations, connection



def _make_deferrable(apps, schema_editor):
    """
    Change the unique constraint to be deferrable
    """
    # Get the db name of the constraint
    MyModel = apps.get_model('myapp', 'MyModel')
    CONSTRAINT_NAME = schema_editor._constraint_names(MYModel,
                                                                   ['col1', 'col2'],
                                                                   unique=True)[0]
    TABLE_NAME = MyModel._meta.db_table


    # Drop then re-add with deferrable as ALTER doesnt seem to work for unique constraints in psql
    with schema_editor.connection.create_cursor() as curs:
        curs.execute(
            f'ALTER TABLE {TABLE_NAME} DROP CONSTRAINT "{CONSTRAINT_NAME}";'
        )
        curs.execute(
            f'ALTER TABLE {TABLE_NAME} ADD CONSTRAINT'
            f' {CONSTRAINT_NAME}'
            f' UNIQUE (col1, col2) DEFERRABLE INITIALLY DEFERRED;'
        )


def _unmake_deferrable(apps, schema_editor):
    """
    Reverse the unique constraint to be not deferrable
    """
    # Get the db name of unique constraint
    MyModel = apps.get_model('myapp', 'MyModel')
    CONSTRAINT_NAME = schema_editor._constraint_names(MyModel,
                                                                   ['col1', 'col2'],
                                                                   unique=True)[0]
    TABLE_NAME = MyModel._meta.db_table

    with schema_editor.connection.create_cursor() as curs:
        curs.execute(
            f'ALTER TABLE {TABLE_NAME} DROP CONSTRAINT "{CONSTRAINT_NAME}";'
        ) 
        curs.execute(
            f'ALTER TABLE {TABLE_NAME} ADD CONSTRAINT'
            f' {CONSTRAINT_NAME}'
            f' UNIQUE (col1, col2) NOT DEFERRABLE;'
        )

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '<previous_mig>'),
    ]

    operations = [
        migrations.RunPython(code=_make_deferrable,  reverse_code=_unmake_deferrable)
    ]

答案 2 :(得分:1)

最近,Django新增了对此功能的支持(请参见ticket)。从Django 3.1开始,您可以编写:

class UniqueConstraintDeferrable(models.Model):
    name = models.CharField(max_length=255)
    shelf = models.CharField(max_length=31)

    class Meta:
        required_db_features = {
            'supports_deferrable_unique_constraints',
        }
        constraints = [
            models.UniqueConstraint(
                fields=['name'],
                name='name_init_deferred_uniq',
                deferrable=models.Deferrable.DEFERRED,
            ),
            models.UniqueConstraint(
                fields=['shelf'],
                name='sheld_init_immediate_uniq',
                deferrable=models.Deferrable.IMMEDIATE,
            ),
        ]