How can I set a table constraint “deferrable initially deferred” in django model?

别等时光非礼了梦想. 提交于 2019-12-01 07:28:34

问题


I am trying to set a constraint to a table model in django with a postgresql database.

I can do it via postgresql with this sentence:

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

But I want to do it via django model. Reading the django official documentation I have not found anything related.

I need something like this:

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

Is it possible to do something like this?


回答1:


Django doesn't support that.

You can do it with custom SQL. In your models.py, add this:

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)

Although I've done such things in the past, I've found that over the years I prefer to keep my work simpler and independent from any specific RDBMS. For example, you really want to support SQLite, because it makes development so much easier. With a little change in design you can often get rid of such stuff.

Update: I think @fpghost's answer is better. I don't know what I was thinking :-)




回答2:


I would do this via a single migration. First programatically get the unique constraint name, then drop and re-add (since altering it seems to only work for FK constraints, not unique constraints). Add reverse migration that undoes this too.

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


来源:https://stackoverflow.com/questions/40891574/how-can-i-set-a-table-constraint-deferrable-initially-deferred-in-django-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!