Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 930
MouTio Avatar asked Nov 30 '16 15:11

MouTio


1 Answers

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)
    ]
like image 125
fpghost Avatar answered Sep 27 '22 21:09

fpghost