diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 656d257..5d0b453 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -10,4 +10,4 @@ jobs: - name: checkout code uses: actions/checkout@v2 - name: run checks - run: docker-compose run --rm django-pg-zero-downtime-migrations-tests tox + run: docker compose run --rm django-pg-zero-downtime-migrations-tests tox diff --git a/CHANGES.md b/CHANGES.md index c6cff14..8c4c06f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # django-pg-zero-downtime-migrations changelog +## 0.17 + - dropped support for Python 3.6 and 3.7 + - dropped support for Django 3.2, 4.0 and 4.1 + ## 0.16 - changed `ADD COLUMN DEFAULT NULL` to safe operation for code default - changed `ADD COLUMN DEFAULT NOT NULL` to safe operation for `db_default` in django 5.0+ diff --git a/Dockerfile b/Dockerfile index ad5577d..c79af14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,6 @@ RUN apt-get install -q -y --no-install-recommends software-properties-common git RUN add-apt-repository ppa:deadsnakes/ppa RUN apt-get update RUN apt-get install -q -y --no-install-recommends \ - python3.6 \ - python3.7 python3.7-distutils \ python3.8 python3.8-distutils \ python3.9 python3.9-distutils \ python3.10 python3.10-distutils \ diff --git a/django_zero_downtime_migrations/__init__.py b/django_zero_downtime_migrations/__init__.py index e9ed7e5..718d000 100644 --- a/django_zero_downtime_migrations/__init__.py +++ b/django_zero_downtime_migrations/__init__.py @@ -1 +1 @@ -__version__ = "0.16" +__version__ = "0.17" diff --git a/django_zero_downtime_migrations/backends/postgres/schema.py b/django_zero_downtime_migrations/backends/postgres/schema.py index c1f0c20..2fee8dc 100644 --- a/django_zero_downtime_migrations/backends/postgres/schema.py +++ b/django_zero_downtime_migrations/backends/postgres/schema.py @@ -9,7 +9,6 @@ from django.db.backends.postgresql.schema import ( DatabaseSchemaEditor as PostgresDatabaseSchemaEditor ) -from django.db.backends.utils import strip_quotes from django.db.models import NOT_PROVIDED @@ -254,17 +253,12 @@ class DatabaseSchemaEditorMixin: "AND convalidated" ) - if django.VERSION[:2] >= (4, 1): - sql_alter_sequence_type = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_alter_sequence_type) - sql_add_identity = PGAccessExclusive( - PostgresDatabaseSchemaEditor.sql_add_identity, - idempotent_condition=Condition(_sql_identity_exists, False), - ) - sql_drop_indentity = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_drop_indentity) - else: - sql_alter_sequence_type = PGAccessExclusive("ALTER SEQUENCE IF EXISTS %(sequence)s AS %(type)s") - sql_create_sequence = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_create_sequence) - sql_set_sequence_owner = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_set_sequence_owner) + sql_alter_sequence_type = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_alter_sequence_type) + sql_add_identity = PGAccessExclusive( + PostgresDatabaseSchemaEditor.sql_add_identity, + idempotent_condition=Condition(_sql_identity_exists, False), + ) + sql_drop_indentity = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_drop_indentity) sql_delete_sequence = PGAccessExclusive(PostgresDatabaseSchemaEditor.sql_delete_sequence) sql_create_table = PGAccessExclusive( PostgresDatabaseSchemaEditor.sql_create_table, @@ -447,9 +441,8 @@ class DatabaseSchemaEditorMixin: PostgresDatabaseSchemaEditor.sql_delete_index_concurrently ) - if django.VERSION[:2] >= (4, 2): - sql_alter_table_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_table_comment) - sql_alter_column_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_column_comment) + sql_alter_table_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_table_comment) + sql_alter_column_comment = PGShareUpdateExclusive(PostgresDatabaseSchemaEditor.sql_alter_column_comment) _sql_column_not_null = MultiStatementSQL( PGAccessExclusive( @@ -845,56 +838,16 @@ def _add_column_primary_key(self, model, field): return "" def _add_column_unique(self, model, field): - if django.VERSION[:2] >= (4, 0): - self.deferred_sql.append(self._create_unique_sql(model, [field])) - else: - self.deferred_sql.append(self._create_unique_sql(model, [field.column])) + self.deferred_sql.append(self._create_unique_sql(model, [field])) return "" - if django.VERSION[:2] <= (3, 2): - def skip_default_on_alter(self, field): - """ - Some backends don't accept default values for certain columns types - (i.e. MySQL longtext and longblob) in the ALTER COLUMN statement. - """ - return False - - def column_sql(self, model, field, include_default=False): - """ - Return the column definition for a field. The field must already have - had set_attributes_from_name() called. - """ - if not include_default: - return super().column_sql(model, field, include_default) - - # Get the column's type and use that as the basis of the SQL. - field_db_params = field.db_parameters(connection=self.connection) - column_db_type = field_db_params["type"] - # Check for fields that aren't actually columns (e.g. M2M). - if column_db_type is None: - return None, None - params = [] - return ( - " ".join( - # This appends to the params being returned. - self._iter_column_sql( - column_db_type, - params, - model, - field, - include_default, - ) - ), - params, - ) - def _patched_iter_column_sql( self, column_db_type, params, model, field, field_db_params, include_default ): yield column_db_type if field_db_params.get("collation"): yield self._collate_sql(field_db_params.get("collation")) - if django.VERSION >= (4, 2) and self.connection.features.supports_comments_inline and field.db_comment: + if self.connection.features.supports_comments_inline and field.db_comment: yield self._comment_sql(field.db_comment) # Work out nullability. null = field.null @@ -955,49 +908,27 @@ def _patched_iter_column_sql( ): yield self.connection.ops.tablespace_sql(tablespace, inline=True) - if django.VERSION >= (4, 1): - def _iter_column_sql( - self, column_db_type, params, model, field, field_db_params, include_default - ): - if not include_default: - yield from super()._iter_column_sql( - column_db_type, - params, - model, - field, - field_db_params, - include_default, - ) - else: - yield from self._patched_iter_column_sql( - column_db_type, - params, - model, - field, - field_db_params, - include_default, - ) - else: - def _iter_column_sql( - self, column_db_type, params, model, field, include_default - ): - if not include_default: - yield from super()._iter_column_sql( - column_db_type, - params, - model, - field, - include_default, - ) - else: - yield from self._patched_iter_column_sql( - column_db_type, - params, - model, - field, - {}, - include_default, - ) + def _iter_column_sql( + self, column_db_type, params, model, field, field_db_params, include_default + ): + if not include_default: + yield from super()._iter_column_sql( + column_db_type, + params, + model, + field, + field_db_params, + include_default, + ) + else: + yield from self._patched_iter_column_sql( + column_db_type, + params, + model, + field, + field_db_params, + include_default, + ) def _alter_column_set_not_null(self, model, new_field): self.deferred_sql.append(self._sql_column_not_null % { @@ -1056,16 +987,7 @@ def _immediate_type_cast(self, old_type, new_type): return new_type_precision >= old_type_precision and new_type_scale == old_type_scale return False - if django.VERSION[:2] < (4, 1): - def _get_sequence_name(self, table, column): - with self.connection.cursor() as cursor: - for sequence in self.connection.introspection.get_sequences(cursor, table): - if sequence["column"] == column: - return sequence["name"] - return None - - # TODO: after django 4.1 support drop replace *args, **kwargs with original signature - def _alter_column_type_sql(self, model, old_field, new_field, new_type, *args, **kwargs): + def _alter_column_type_sql(self, model, old_field, new_field, new_type, old_collation, new_collation): old_db_params = old_field.db_parameters(connection=self.connection) old_type = old_db_params["type"] if not self._immediate_type_cast(old_type, new_type): @@ -1073,51 +995,7 @@ def _alter_column_type_sql(self, model, old_field, new_field, new_type, *args, * raise UnsafeOperationException(Unsafe.ALTER_COLUMN_TYPE) else: warnings.warn(UnsafeOperationWarning(Unsafe.ALTER_COLUMN_TYPE)) - if django.VERSION[:2] < (4, 1): - # old django versions runs in transaction next queries for autofield type changes: - # - alter column type - # - drop sequence with old type - # - create sequence with new type - # - alter column set default - # - set sequence current value - # - set sequence to field - # if we run this queries without transaction - # then concurrent insertions between drop sequence and end of migration can fail - # so simplify migration to two safe steps: alter colum type and alter sequence type - serial_fields_map = { - "bigserial": "bigint", - "serial": "integer", - "smallserial": "smallint", - } - if new_type.lower() in serial_fields_map: - column = strip_quotes(new_field.column) - table = strip_quotes(model._meta.db_table) - sequence_name = self._get_sequence_name(table, column) - if sequence_name is not None: - using_sql = "" - if self._field_data_type(old_field) != self._field_data_type(new_field): - using_sql = " USING %(column)s::%(type)s" - return ( - ( - (self.sql_alter_column_type + using_sql) - % { - "column": self.quote_name(column), - "type": serial_fields_map[new_type.lower()], - }, - [], - ), - [ - ( - self.sql_alter_sequence_type - % { - "sequence": self.quote_name(sequence_name), - "type": serial_fields_map[new_type.lower()], - }, - [], - ), - ], - ) - return super()._alter_column_type_sql(model, old_field, new_field, new_type, *args, **kwargs) + return super()._alter_column_type_sql(model, old_field, new_field, new_type, old_collation, new_collation) class DatabaseSchemaEditor(DatabaseSchemaEditorMixin, PostgresDatabaseSchemaEditor): diff --git a/docker-compose.yml b/docker-compose.yml index d62b0fb..3ecba04 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.7" services: pg16: image: postgres:16-alpine diff --git a/setup.py b/setup.py index 96f43fb..019ea59 100644 --- a/setup.py +++ b/setup.py @@ -35,24 +35,19 @@ def _get_long_description(): 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Framework :: Django', - 'Framework :: Django :: 3.2', - 'Framework :: Django :: 4.0', - 'Framework :: Django :: 4.1', 'Framework :: Django :: 4.2', 'Framework :: Django :: 5.0', ], keywords='django postgres postgresql migrations', packages=find_packages(exclude=['manage*', 'tests*']), - python_requires='>=3.6', + python_requires='>=3.8', install_requires=[ - 'django>=3.2', + 'django>=4.2', ] ) diff --git a/tests/integration/test_migrations.py b/tests/integration/test_migrations.py index 17f3509..13ac177 100644 --- a/tests/integration/test_migrations.py +++ b/tests/integration/test_migrations.py @@ -191,10 +191,6 @@ def test_good_flow_drop_table_with_constraints(): @skip_for_default_django_backend -@pytest.mark.skipif( - django.VERSION[:2] < (4, 0), - reason="django before 4.0 case", -) @pytest.mark.django_db(transaction=True) @modify_settings(INSTALLED_APPS={"append": "tests.apps.good_flow_drop_column_with_constraints"}) @override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True) @@ -499,14 +495,6 @@ def test_idempotency_create_table(): "test_model_id" integer NULL ); """) - if django.VERSION[:2] < (4, 1): - _create_table_sql = one_line_sql(""" - CREATE TABLE "idempotency_create_table_app_relatedtesttable" ( - "id" serial NOT NULL PRIMARY KEY, - "test_field_int" integer NULL, - "test_model_id" integer NULL - ); - """) _create_unique_index_sql = one_line_sql(""" CREATE UNIQUE INDEX CONCURRENTLY "idempotency_create_table_app_relatedtesttable_uniq" ON "idempotency_create_table_app_relatedtesttable" ("test_model_id", "test_field_int"); @@ -1805,15 +1793,6 @@ def test_idempotency_add_primary_key(): ALTER TABLE "idempotency_add_primary_key_app_relatedtesttable" DROP COLUMN "id" CASCADE; """) - _create_unique_index_sql_before_django41 = one_line_sql(""" - CREATE UNIQUE INDEX CONCURRENTLY "idempotency_add_primary__test_field_int_e9cebf24_uniq" - ON "idempotency_add_primary_key_app_relatedtesttable" ("test_field_int"); - """) - _create_unique_constraint_sql_before_django41 = one_line_sql(""" - ALTER TABLE "idempotency_add_primary_key_app_relatedtesttable" - ADD CONSTRAINT "idempotency_add_primary__test_field_int_e9cebf24_uniq" - UNIQUE USING INDEX "idempotency_add_primary__test_field_int_e9cebf24_uniq"; - """) _create_unique_index_sql = one_line_sql(""" CREATE UNIQUE INDEX CONCURRENTLY "idempotency_add_primary_k_test_field_int_e9cebf24_pk" ON "idempotency_add_primary_key_app_relatedtesttable" ("test_field_int"); @@ -1865,20 +1844,11 @@ def test_idempotency_add_primary_key(): call_command("migrate", "idempotency_add_primary_key_app", "0001") with override_settings(ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=False): migration_sql = call_command("sqlmigrate", "idempotency_add_primary_key_app", "0002") - if django.VERSION[:2] < (4, 1): - assert split_sql_queries(migration_sql) == [ - _drop_column_sql, - _create_unique_index_sql_before_django41, - _create_unique_constraint_sql_before_django41, - _create_unique_index_sql, - _create_primary_key_sql, - ] - else: - assert split_sql_queries(migration_sql) == [ - _drop_column_sql, - _create_unique_index_sql, - _create_primary_key_sql, - ] + assert split_sql_queries(migration_sql) == [ + _drop_column_sql, + _create_unique_index_sql, + _create_primary_key_sql, + ] # migrate case 1 with override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=False): @@ -2022,10 +1992,6 @@ def old_schema_compatible(dump: str) -> str: @skip_for_default_django_backend -@pytest.mark.skipif( - django.VERSION[:2] < (4, 1), - reason="django after 4.1 case", -) @pytest.mark.django_db(transaction=True) @modify_settings(INSTALLED_APPS={"append": "tests.apps.idempotency_add_auto_field_app"}) @override_settings(ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE=True) diff --git a/tests/unit/test_schema.py b/tests/unit/test_schema.py index 873e7a2..1c526d5 100644 --- a/tests/unit/test_schema.py +++ b/tests/unit/test_schema.py @@ -111,17 +111,11 @@ def test_create_model__ok(): with cmp_schema_editor() as editor: editor.create_model(Model) assert editor.collected_sql == editor.django_sql - if django.VERSION[:2] >= (4, 1): - assert editor.django_sql == [ - 'CREATE TABLE "tests_model" ' - '("id" integer NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, ' - '"field1" integer NOT NULL, "field2" integer NOT NULL);', - ] - else: - assert editor.django_sql == [ - 'CREATE TABLE "tests_model" ' - '("id" serial NOT NULL PRIMARY KEY, "field1" integer NOT NULL, "field2" integer NOT NULL);', - ] + assert editor.django_sql == [ + 'CREATE TABLE "tests_model" ' + '("id" integer NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, ' + '"field1" integer NOT NULL, "field2" integer NOT NULL);', + ] @pytest.mark.django_db @@ -580,38 +574,15 @@ def test_alter_field_type_integer_to_integer_identity__ok(): new_field.set_attributes_from_name('field') new_field.model = Model editor.alter_field(Model, old_field, new_field) - if django.VERSION[:2] >= (4, 1): - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer;', - ) + timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer;', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', - ] - else: - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer USING "field"::integer;', - ) + timeouts( - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - ) + timeouts( - 'CREATE SEQUENCE "tests_model_field_seq";', - ) + timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" SET DEFAULT nextval(\'"tests_model_field_seq"\');', - ) + [ - 'SELECT setval(\'"tests_model_field_seq"\', MAX("field")) FROM "tests_model";', - ] + timeouts( - 'ALTER SEQUENCE "tests_model_field_seq" OWNED BY "tests_model"."field";', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer USING "field"::integer;', - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - 'CREATE SEQUENCE "tests_model_field_seq";', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" SET DEFAULT nextval(\'"tests_model_field_seq"\');', - 'SELECT setval(\'"tests_model_field_seq"\', MAX("field")) FROM "tests_model";', - 'ALTER SEQUENCE "tests_model_field_seq" OWNED BY "tests_model"."field";', - ] + assert editor.collected_sql == timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer;', + ) + timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', + ) + assert editor.django_sql == [ + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE integer;', + 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', + ] @pytest.mark.django_db @@ -658,38 +629,15 @@ def test_alter_field_type_integer_to_bigint_identity__warning(): new_field.set_attributes_from_name('field') new_field.model = Model editor.alter_field(Model, old_field, new_field) - if django.VERSION[:2] >= (4, 1): - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', - ] - else: - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - ) + timeouts( - 'CREATE SEQUENCE "tests_model_field_seq";', - ) + timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" SET DEFAULT nextval(\'"tests_model_field_seq"\');', - ) + [ - 'SELECT setval(\'"tests_model_field_seq"\', MAX("field")) FROM "tests_model";', - ] + timeouts( - 'ALTER SEQUENCE "tests_model_field_seq" OWNED BY "tests_model"."field";', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - 'CREATE SEQUENCE "tests_model_field_seq";', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" SET DEFAULT nextval(\'"tests_model_field_seq"\');', - 'SELECT setval(\'"tests_model_field_seq"\', MAX("field")) FROM "tests_model";', - 'ALTER SEQUENCE "tests_model_field_seq" OWNED BY "tests_model"."field";', - ] + assert editor.collected_sql == timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + ) + timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', + ) + assert editor.django_sql == [ + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + 'ALTER TABLE "tests_model" ALTER COLUMN "field" ADD GENERATED BY DEFAULT AS IDENTITY;', + ] @pytest.mark.django_db @@ -736,29 +684,18 @@ def test_alter_field_type_integer_identity_to_bigint__warning(mocker): new_field.set_attributes_from_name('field') new_field.model = Model editor.alter_field(Model, old_field, new_field) - if django.VERSION[:2] >= (4, 1): - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" DROP IDENTITY IF EXISTS;', - ) + timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'DROP SEQUENCE IF EXISTS "field_seq" CASCADE;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" DROP IDENTITY IF EXISTS;', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'DROP SEQUENCE IF EXISTS "field_seq" CASCADE;', - ] - else: - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - ] + assert editor.collected_sql == timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" DROP IDENTITY IF EXISTS;', + ) + timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + ) + timeouts( + 'DROP SEQUENCE IF EXISTS "field_seq" CASCADE;', + ) + assert editor.django_sql == [ + 'ALTER TABLE "tests_model" ALTER COLUMN "field" DROP IDENTITY IF EXISTS;', + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + 'DROP SEQUENCE IF EXISTS "field_seq" CASCADE;', + ] @pytest.mark.django_db @@ -807,30 +744,15 @@ def test_alter_field_type_integer_identity_to_bigint_identity__warning(mocker): new_field.set_attributes_from_name('field') new_field.model = Model editor.alter_field(Model, old_field, new_field) - if django.VERSION[:2] >= (4, 1): - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'ALTER SEQUENCE IF EXISTS "field_seq" AS bigint;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'ALTER SEQUENCE IF EXISTS "field_seq" AS bigint;', - ] - else: - assert editor.collected_sql == timeouts( - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - ) + timeouts( - 'ALTER SEQUENCE IF EXISTS "field_seq" AS bigint;', - ) - assert editor.django_sql == [ - 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', - 'DROP SEQUENCE IF EXISTS "tests_model_field_seq" CASCADE;', - 'CREATE SEQUENCE "tests_model_field_seq";', - 'ALTER TABLE "tests_model" ALTER COLUMN "field" SET DEFAULT nextval(\'"tests_model_field_seq"\');', - 'SELECT setval(\'"tests_model_field_seq"\', MAX("field")) FROM "tests_model";', - 'ALTER SEQUENCE "tests_model_field_seq" OWNED BY "tests_model"."field";', - ] + assert editor.collected_sql == timeouts( + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + ) + timeouts( + 'ALTER SEQUENCE IF EXISTS "field_seq" AS bigint;', + ) + assert editor.django_sql == [ + 'ALTER TABLE "tests_model" ALTER COLUMN "field" TYPE bigint USING "field"::bigint;', + 'ALTER SEQUENCE IF EXISTS "field_seq" AS bigint;', + ] @pytest.mark.django_db @@ -1949,24 +1871,14 @@ def test_add_meta_conditional_multicolumn_unique_constraint__ok(): with cmp_schema_editor() as editor: editor.add_constraint(Model, models.UniqueConstraint( fields=('field1', 'field2'), name='field1_field2_uniq', condition=models.Q(field1=models.F('field2')))) - if django.VERSION[:2] >= (4, 0): - assert editor.collected_sql == [ - 'CREATE UNIQUE INDEX CONCURRENTLY "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' - 'WHERE "field1" = ("field2");', - ] - assert editor.django_sql == [ - 'CREATE UNIQUE INDEX "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' - 'WHERE "field1" = ("field2");', - ] - else: - assert editor.collected_sql == [ - 'CREATE UNIQUE INDEX CONCURRENTLY "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' - 'WHERE "field1" = "field2";', - ] - assert editor.django_sql == [ - 'CREATE UNIQUE INDEX "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' - 'WHERE "field1" = "field2";', - ] + assert editor.collected_sql == [ + 'CREATE UNIQUE INDEX CONCURRENTLY "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' + 'WHERE "field1" = ("field2");', + ] + assert editor.django_sql == [ + 'CREATE UNIQUE INDEX "field1_field2_uniq" ON "tests_model" ("field1", "field2") ' + 'WHERE "field1" = ("field2");', + ] @pytest.mark.django_db diff --git a/tox.ini b/tox.ini index 1488bdc..c84b39b 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,6 @@ envlist = py{3.10,3.11,3.12}-django{5.0}-psycopg{2,3} py{3.8,3.9,3.10,3.11,3.12}-django{4.2}-psycopg{2,3} - py{3.8,3.9,3.10,3.11}-django{4.0,4.1} - py{3.6,3.7,3.8,3.9,3.10}-django{3.2} [testenv] usedevelop = True @@ -15,12 +13,6 @@ commands = py{3.8,3.9,3.10,3.11,3.12}-django{4.2,5.0}-psycopg{2,3}: bash -c "DB_HOST=pg16 DB_USER=test pytest tests/unit" py{3.8,3.9,3.10,3.11,3.12}-django{4.2,5.0}-psycopg{2,3}: bash -c "DB_HOST=postgis16 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/unit" - py{3.8,3.9,3.10,3.11}-django{4.0,4.1}: bash -c "DB_HOST=pg16 DB_USER=test pytest tests/unit" - py{3.8,3.9,3.10,3.11}-django{4.0,4.1}: bash -c "DB_HOST=postgis16 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/unit" - - py{3.6,3.7,3.8,3.9,3.10}-django{3.2}: bash -c "DB_HOST=pg16 DB_USER=test pytest tests/unit" - py{3.6,3.7,3.8,3.9,3.10}-django{3.2}: bash -c "DB_HOST=postgis16 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/unit" - py{3.12}-django{5.0}-psycopg{3}: bash -c "DB_HOST=pg16 DB_USER=test DB_ENGINE=django.db.backends.postgresql pytest tests/integration" py{3.12}-django{5.0}-psycopg{3}: bash -c "DB_HOST=pg16 DB_USER=test DB_SUPER_USER=root pytest tests/integration" py{3.12}-django{5.0}-psycopg{3}: bash -c "DB_HOST=postgis16 DB_USER=root DB_ENGINE=django_zero_downtime_migrations.backends.postgis pytest tests/integration" @@ -31,8 +23,6 @@ commands = py{3.12}-django{5.0}-psycopg{3}: bash -c "DB_HOST=pg12 DB_USER=test DB_SUPER_USER=root pytest tests/integration" py{3.12}-django{4.2}-psycopg{3}: bash -c "DB_HOST=pg16 DB_USER=test DB_SUPER_USER=root pytest tests/integration" - py{3.11}-django{4.0,4.1}: bash -c "DB_HOST=pg16 DB_USER=test DB_SUPER_USER=root pytest tests/integration" - py{3.10}-django{3.2}: bash -c "DB_HOST=pg16 DB_USER=test DB_SUPER_USER=root pytest tests/integration" deps = py{3.12}-django{5.0}-psycopg{3}: flake8 @@ -42,12 +32,8 @@ deps = pytest-django pytest-mock - django{3.2,4.0,4.1}: psycopg2-binary psycopg2: psycopg2-binary psycopg3: psycopg[binary] - django3.2: django>=3.2,<4.0 - django4.0: django>=4.0,<4.1 - django4.1: django>=4.1,<4.2 django4.2: django>=4.2,<5.0 django5.0: django>=5.0,<5.1