Repository: tortoise/aerich (language: python)

Issue:
Changing OneToOneField to ForeignKeyField generates broken SQL
# What I done to make it happen
1. Make model which references other model as One2One. One2One is implemented via foreign + unique
2. Migrate & apply changes to db
3. Change field type to ForeignKeyField and migrate

# Expected results
Upgrade: `alter table ... drop constraint <unique_constrait>;`
Downgrade: `alter table add constraint ...;`

# Actual results
Upgrade: aerich tries to drop non-existing index (dropping existing one will not work anyway) and then create new foreign key constraint (ignoring already existing)
Downgrade: drop duplicated constraint + `CREATE UNIQUE INDEX ...`

# Environment info
I'm using latest aerich from PyPI (0.7.1) and PostgreSQL 15


Discussion / hints:

Fix patch:
diff --git a/aerich/ddl/postgres/__init__.py b/aerich/ddl/postgres/__init__.py
index be09212..7834661 100644
--- a/aerich/ddl/postgres/__init__.py
+++ b/aerich/ddl/postgres/__init__.py
@@ -10,7 +10,7 @@ class PostgresDDL(BaseDDL):
     schema_generator_cls = AsyncpgSchemaGenerator
     DIALECT = AsyncpgSchemaGenerator.DIALECT
     _ADD_INDEX_TEMPLATE = 'CREATE {unique}INDEX "{index_name}" ON "{table_name}" ({column_names})'
-    _DROP_INDEX_TEMPLATE = 'DROP INDEX "{index_name}"'
+    _DROP_INDEX_TEMPLATE = 'DROP INDEX IF EXISTS "{index_name}"'
     _ALTER_NULL_TEMPLATE = 'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" {set_drop} NOT NULL'
     _MODIFY_COLUMN_TEMPLATE = (
         'ALTER TABLE "{table_name}" ALTER COLUMN "{column}" TYPE {datatype}{using}'


Test patch:
diff --git a/tests/test_ddl.py b/tests/test_ddl.py
index 5a8d659..417eb92 100644
--- a/tests/test_ddl.py
+++ b/tests/test_ddl.py
@@ -174,9 +174,6 @@ def test_drop_index():
     if isinstance(Migrate.ddl, MysqlDDL):
         assert ret == "ALTER TABLE `category` DROP INDEX `idx_category_name_8b0cb9`"
         assert ret_u == "ALTER TABLE `category` DROP INDEX `name`"
-    elif isinstance(Migrate.ddl, PostgresDDL):
-        assert ret == 'DROP INDEX "idx_category_name_8b0cb9"'
-        assert ret_u == 'DROP INDEX "uid_category_name_8b0cb9"'
     else:
         assert ret == 'DROP INDEX IF EXISTS "idx_category_name_8b0cb9"'
         assert ret_u == 'DROP INDEX IF EXISTS "uid_category_name_8b0cb9"'
diff --git a/tests/test_migrate.py b/tests/test_migrate.py
index 651538e..1f7802a 100644
--- a/tests/test_migrate.py
+++ b/tests/test_migrate.py
@@ -946,14 +946,14 @@ def test_migrate(mocker: MockerFixture):
 
     elif isinstance(Migrate.ddl, PostgresDDL):
         expected_upgrade_operators = {
-            'DROP INDEX "uid_category_title_f7fc03"',
+            'DROP INDEX IF EXISTS "uid_category_title_f7fc03"',
             'ALTER TABLE "category" ALTER COLUMN "name" DROP NOT NULL',
             'ALTER TABLE "category" ALTER COLUMN "slug" TYPE VARCHAR(100) USING "slug"::VARCHAR(100)',
             'ALTER TABLE "category" ALTER COLUMN "created_at" TYPE TIMESTAMPTZ USING "created_at"::TIMESTAMPTZ',
             'ALTER TABLE "category" RENAME COLUMN "user_id" TO "owner_id"',
             'ALTER TABLE "category" ADD CONSTRAINT "fk_category_user_110d4c63" FOREIGN KEY ("owner_id") REFERENCES "user" ("id") ON DELETE CASCADE',
             'ALTER TABLE "config" DROP COLUMN "name"',
-            'DROP INDEX "uid_config_name_2c83c8"',
+            'DROP INDEX IF EXISTS "uid_config_name_2c83c8"',
             'ALTER TABLE "config" ADD "user_id" INT NOT NULL',
             'ALTER TABLE "config" ADD CONSTRAINT "fk_config_user_17daa970" FOREIGN KEY ("user_id") REFERENCES "user" ("id") ON DELETE CASCADE',
             'ALTER TABLE "config" ALTER COLUMN "status" DROP DEFAULT',
@@ -962,7 +962,7 @@ def test_migrate(mocker: MockerFixture):
             'ALTER TABLE "email" ADD "address" VARCHAR(200) NOT NULL',
             'ALTER TABLE "email" RENAME COLUMN "id" TO "email_id"',
             'ALTER TABLE "email" ALTER COLUMN "is_primary" TYPE BOOL USING "is_primary"::BOOL',
-            'DROP INDEX "uid_product_uuid_d33c18"',
+            'DROP INDEX IF EXISTS "uid_product_uuid_d33c18"',
             'ALTER TABLE "product" DROP COLUMN "uuid"',
             'ALTER TABLE "product" ALTER COLUMN "view_num" SET DEFAULT 0',
             'ALTER TABLE "product" RENAME COLUMN "image" TO "pic"',
@@ -1016,10 +1016,10 @@ def test_migrate(mocker: MockerFixture):
             'ALTER TABLE "product" ALTER COLUMN "is_reviewed" TYPE BOOL USING "is_reviewed"::BOOL',
             'ALTER TABLE "product" ALTER COLUMN "body" TYPE TEXT USING "body"::TEXT',
             'DROP TABLE IF EXISTS "product_user"',
-            'DROP INDEX "idx_product_name_869427"',
-            'DROP INDEX "idx_email_email_4a1a33"',
-            'DROP INDEX "uid_user_usernam_9987ab"',
-            'DROP INDEX "uid_product_name_869427"',
+            'DROP INDEX IF EXISTS "idx_product_name_869427"',
+            'DROP INDEX IF EXISTS "idx_email_email_4a1a33"',
+            'DROP INDEX IF EXISTS "uid_user_usernam_9987ab"',
+            'DROP INDEX IF EXISTS "uid_product_name_869427"',
             'DROP TABLE IF EXISTS "email_user"',
             'DROP TABLE IF EXISTS "newmodel"',
         }