diff --git a/CHANGES.md b/CHANGES.md index ee2a0e5..07e3c6b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ # django-pg-zero-downtime-migrations changelog ## 0.14 + - fix deferred sql errors - drop postgres 11 support - mark `migrate_isnotnull_check_constraints` command deprecated diff --git a/django_zero_downtime_migrations/backends/postgres/schema.py b/django_zero_downtime_migrations/backends/postgres/schema.py index 92745a5..48c4b87 100644 --- a/django_zero_downtime_migrations/backends/postgres/schema.py +++ b/django_zero_downtime_migrations/backends/postgres/schema.py @@ -241,6 +241,7 @@ def __init__(self, connection, collect_sql=False, atomic=True): self.FLEXIBLE_STATEMENT_TIMEOUT = getattr( settings, "ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT", False) self.RAISE_FOR_UNSAFE = getattr(settings, "ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE", False) + self.DEFERRED_SQL = getattr(settings, "ZERO_DOWNTIME_MIGRATIONS_DEFERRED_SQL", True) def execute(self, sql, params=()): if sql is DUMMY_SQL: @@ -296,12 +297,19 @@ def _set_operation_timeout(self, statement_timeout=None, lock_timeout=None): self.execute(self.sql_set_lock_timeout % {"lock_timeout": previous_lock_timeout}) def _flush_deferred_sql(self): - """As some alternative sql use deferred sql and deferred sql run after all operations in miration module + """As some alternative sql use deferred sql and deferred sql run after all operations in migration module so good idea to run deferred sql as soon as possible to provide similar as possible state - between operations in migration module.""" - for sql in self.deferred_sql: - self.execute(sql) - self.deferred_sql.clear() + between operations in migration module. But this approach can be reason of errors for some migrations. + + As only constraints creation placed in deferred sql + it looks safe to keep standard django deferred sql run approach. + + # TODO: drop option to run deferred sql as soon as possible in future + """ + if not self.DEFERRED_SQL: + for sql in self.deferred_sql: + self.execute(sql) + self.deferred_sql.clear() def create_model(self, model): super().create_model(model) diff --git a/tests/apps/good_flow_app/migrations/0004_set_field_not_null.py b/tests/apps/good_flow_app/migrations/0004_set_field_not_null.py index 5d39342..ba69069 100644 --- a/tests/apps/good_flow_app/migrations/0004_set_field_not_null.py +++ b/tests/apps/good_flow_app/migrations/0004_set_field_not_null.py @@ -3,6 +3,11 @@ from django.db import IntegrityError, migrations, models +def flush_deferred_sql(apps, schema_editor): + for sql in schema_editor.deferred_sql: + schema_editor.execute(sql) + + def update_objects(apps, schema_editor): db_alias = schema_editor.connection.alias TestTable = apps.get_model('good_flow_app', 'TestTable') @@ -37,5 +42,6 @@ class Migration(migrations.Migration): name='field', field=models.IntegerField(default=0), ), + migrations.RunPython(flush_deferred_sql, migrations.RunPython.noop), migrations.RunPython(insert_objects_and_not_null_check, migrations.RunPython.noop), ] diff --git a/tests/settings.py b/tests/settings.py index d926ae4..9b1a0ee 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -51,6 +51,15 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] +MIGRATION_MODULES = { + 'admin': None, + 'auth': None, + 'contenttypes': None, + 'sessions': None, + 'messages': None, + 'staticfiles': None, +} + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates',