From e9716538519c07d46a2dc6cba87fd096758c9471 Mon Sep 17 00:00:00 2001 From: Waket Zheng Date: Wed, 5 Jun 2024 23:37:30 +0800 Subject: [PATCH] fix: mysql drop unique index migrate error --- aerich/ddl/mysql/__init__.py | 31 +++++++++++++++++++++++++++++++ aerich/migrate.py | 3 ++- tests/test_ddl.py | 6 ++---- tests/test_migrate.py | 17 +++++++++++------ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/aerich/ddl/mysql/__init__.py b/aerich/ddl/mysql/__init__.py index 2eea288..5ecb51d 100644 --- a/aerich/ddl/mysql/__init__.py +++ b/aerich/ddl/mysql/__init__.py @@ -1,7 +1,12 @@ +from typing import TYPE_CHECKING, List, Type + from tortoise.backends.mysql.schema_generator import MySQLSchemaGenerator from aerich.ddl import BaseDDL +if TYPE_CHECKING: + from tortoise import Model # noqa:F401 + class MysqlDDL(BaseDDL): schema_generator_cls = MySQLSchemaGenerator @@ -30,3 +35,29 @@ class MysqlDDL(BaseDDL): ) _MODIFY_COLUMN_TEMPLATE = "ALTER TABLE `{table_name}` MODIFY COLUMN {column}" _RENAME_TABLE_TEMPLATE = "ALTER TABLE `{old_table_name}` RENAME TO `{new_table_name}`" + + def _index_name(self, unique: bool, model: "Type[Model]", field_names: List[str]) -> str: + if unique: + if len(field_names) == 1: + # Example: `email = CharField(max_length=50, unique=True)` + # Generate schema: `"email" VARCHAR(10) NOT NULL UNIQUE` + # Unique index key is the same as field name: `email` + return field_names[0] + index_prefix = "uid" + else: + index_prefix = "idx" + return self.schema_generator._generate_index_name(index_prefix, model, field_names) + + def add_index(self, model: "Type[Model]", field_names: List[str], unique=False) -> str: + return self._ADD_INDEX_TEMPLATE.format( + unique="UNIQUE " if unique else "", + index_name=self._index_name(unique, model, field_names), + table_name=model._meta.db_table, + column_names=", ".join(self.schema_generator.quote(f) for f in field_names), + ) + + def drop_index(self, model: "Type[Model]", field_names: List[str], unique=False) -> str: + return self._DROP_INDEX_TEMPLATE.format( + index_name=self._index_name(unique, model, field_names), + table_name=model._meta.db_table, + ) diff --git a/aerich/migrate.py b/aerich/migrate.py index 32cad7f..d5ef6e6 100644 --- a/aerich/migrate.py +++ b/aerich/migrate.py @@ -477,12 +477,13 @@ def diff_models(cls, old_models: Dict[str, dict], new_models: Dict[str, dict], u _, option, old_new = change if option == "indexed": # change index - unique = new_data_field.get("unique") if old_new[0] is False and old_new[1] is True: + unique = new_data_field.get("unique") cls._add_operator( cls._add_index(model, (field_name,), unique), upgrade, True ) else: + unique = old_data_field.get("unique") cls._add_operator( cls._drop_index(model, (field_name,), unique), upgrade, True ) diff --git a/tests/test_ddl.py b/tests/test_ddl.py index 6bf1cb0..b04e5d7 100644 --- a/tests/test_ddl.py +++ b/tests/test_ddl.py @@ -151,9 +151,7 @@ def test_add_index(): index_u = Migrate.ddl.add_index(Category, ["name"], True) if isinstance(Migrate.ddl, MysqlDDL): assert index == "ALTER TABLE `category` ADD INDEX `idx_category_name_8b0cb9` (`name`)" - assert ( - index_u == "ALTER TABLE `category` ADD UNIQUE INDEX `uid_category_name_8b0cb9` (`name`)" - ) + assert index_u == "ALTER TABLE `category` ADD UNIQUE INDEX `name` (`name`)" elif isinstance(Migrate.ddl, PostgresDDL): assert index == 'CREATE INDEX "idx_category_name_8b0cb9" ON "category" ("name")' assert index_u == 'CREATE UNIQUE INDEX "uid_category_name_8b0cb9" ON "category" ("name")' @@ -169,7 +167,7 @@ def test_drop_index(): ret_u = Migrate.ddl.drop_index(Category, ["name"], True) if isinstance(Migrate.ddl, MysqlDDL): assert ret == "ALTER TABLE `category` DROP INDEX `idx_category_name_8b0cb9`" - assert ret_u == "ALTER TABLE `category` DROP INDEX `uid_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"' diff --git a/tests/test_migrate.py b/tests/test_migrate.py index 9269376..d51deb0 100644 --- a/tests/test_migrate.py +++ b/tests/test_migrate.py @@ -44,8 +44,8 @@ "python_type": "str", "generated": False, "nullable": False, - "unique": False, - "indexed": False, + "unique": True, + "indexed": True, "default": None, "description": None, "docstring": None, @@ -786,7 +786,8 @@ def test_migrate(mocker: MockerFixture): - drop field: User.avatar - add index: Email.email - add many to many: Email.users - - remove unique: User.username + - remove unique: Category.slug + - add unique: User.username - change column: length User.password - add unique_together: (name,type) of Product - alter default: Config.status @@ -806,6 +807,7 @@ def test_migrate(mocker: MockerFixture): Migrate._merge_operators() if isinstance(Migrate.ddl, MysqlDDL): expected_upgrade_operators = { + "ALTER TABLE `category` DROP INDEX `slug`", "ALTER TABLE `category` MODIFY COLUMN `name` VARCHAR(200)", "ALTER TABLE `category` MODIFY COLUMN `slug` VARCHAR(100) NOT NULL", "ALTER TABLE `config` ADD `user_id` INT NOT NULL COMMENT 'User'", @@ -830,7 +832,7 @@ def test_migrate(mocker: MockerFixture): "ALTER TABLE `user` MODIFY COLUMN `is_active` BOOL NOT NULL COMMENT 'Is Active' DEFAULT 1", "ALTER TABLE `user` MODIFY COLUMN `is_superuser` BOOL NOT NULL COMMENT 'Is SuperUser' DEFAULT 0", "ALTER TABLE `user` MODIFY COLUMN `longitude` DECIMAL(10,8) NOT NULL", - "ALTER TABLE `user` ADD UNIQUE INDEX `uid_user_usernam_9987ab` (`username`)", + "ALTER TABLE `user` ADD UNIQUE INDEX `username` (`username`)", "CREATE TABLE `email_user` (\n `email_id` INT NOT NULL REFERENCES `email` (`email_id`) ON DELETE CASCADE,\n `user_id` INT NOT NULL REFERENCES `user` (`id`) ON DELETE CASCADE\n) CHARACTER SET utf8mb4", "CREATE TABLE IF NOT EXISTS `newmodel` (\n `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,\n `name` VARCHAR(50) NOT NULL\n) CHARACTER SET utf8mb4", "ALTER TABLE `category` MODIFY COLUMN `created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)", @@ -838,6 +840,7 @@ def test_migrate(mocker: MockerFixture): "ALTER TABLE `email` MODIFY COLUMN `is_primary` BOOL NOT NULL DEFAULT 0", } expected_downgrade_operators = { + "ALTER TABLE `category` ADD UNIQUE INDEX `slug` (`slug`)", "ALTER TABLE `category` MODIFY COLUMN `name` VARCHAR(200) NOT NULL", "ALTER TABLE `category` MODIFY COLUMN `slug` VARCHAR(200) NOT NULL", "ALTER TABLE `config` DROP COLUMN `user_id`", @@ -853,7 +856,7 @@ def test_migrate(mocker: MockerFixture): "ALTER TABLE `product` DROP INDEX `uid_product_name_869427`", "ALTER TABLE `product` ALTER COLUMN `view_num` DROP DEFAULT", "ALTER TABLE `user` ADD `avatar` VARCHAR(200) NOT NULL DEFAULT ''", - "ALTER TABLE `user` DROP INDEX `idx_user_usernam_9987ab`", + "ALTER TABLE `user` DROP INDEX `username`", "ALTER TABLE `user` MODIFY COLUMN `password` VARCHAR(200) NOT NULL", "DROP TABLE IF EXISTS `email_user`", "DROP TABLE IF EXISTS `newmodel`", @@ -877,6 +880,7 @@ def test_migrate(mocker: MockerFixture): elif isinstance(Migrate.ddl, PostgresDDL): expected_upgrade_operators = { + 'DROP INDEX "uid_category_slug_e9bcff"', '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', @@ -909,6 +913,7 @@ def test_migrate(mocker: MockerFixture): 'CREATE UNIQUE INDEX "uid_user_usernam_9987ab" ON "user" ("username")', } expected_downgrade_operators = { + 'CREATE UNIQUE INDEX "uid_category_slug_e9bcff" ON "category" ("slug")', 'ALTER TABLE "category" ALTER COLUMN "name" SET NOT NULL', 'ALTER TABLE "category" ALTER COLUMN "slug" TYPE VARCHAR(200) USING "slug"::VARCHAR(200)', 'ALTER TABLE "category" ALTER COLUMN "created_at" TYPE TIMESTAMPTZ USING "created_at"::TIMESTAMPTZ', @@ -935,7 +940,7 @@ def test_migrate(mocker: MockerFixture): 'ALTER TABLE "product" ALTER COLUMN "body" TYPE TEXT USING "body"::TEXT', 'DROP INDEX "idx_product_name_869427"', 'DROP INDEX "idx_email_email_4a1a33"', - 'DROP INDEX "idx_user_usernam_9987ab"', + 'DROP INDEX "uid_user_usernam_9987ab"', 'DROP INDEX "uid_product_name_869427"', 'DROP TABLE IF EXISTS "email_user"', 'DROP TABLE IF EXISTS "newmodel"',