diff --git a/aerich/migrate.py b/aerich/migrate.py index a69a97c..9e2a88a 100644 --- a/aerich/migrate.py +++ b/aerich/migrate.py @@ -2,8 +2,7 @@ import os from datetime import datetime from pathlib import Path from typing import Dict, List, Optional, Tuple, Type - -import click +from dictdiffer import diff from tortoise import ( BackwardFKRelation, BackwardOneToOneRelation, @@ -191,7 +190,7 @@ class Migrate: model = cls._get_model(new_model_describe.get("name").split(".")[1]) # add fields for new_data_field_name in set(new_data_fields_name).difference( - set(old_data_fields_name) + set(old_data_fields_name) ): cls._add_operator( cls._add_field( @@ -206,7 +205,7 @@ class Migrate: ) # remove fields for old_data_field_name in set(old_data_fields_name).difference( - set(new_data_fields_name) + set(new_data_fields_name) ): cls._add_operator( cls._remove_field( @@ -227,7 +226,7 @@ class Migrate: # add fk for new_fk_field_name in set(new_fk_fields_name).difference( - set(old_fk_fields_name) + set(old_fk_fields_name) ): fk_field = next( filter(lambda x: x.get("name") == new_fk_field_name, new_fk_fields) @@ -238,7 +237,7 @@ class Migrate: ) # drop fk for old_fk_field_name in set(old_fk_fields_name).difference( - set(new_fk_fields_name) + set(new_fk_fields_name) ): old_fk_field = next( filter(lambda x: x.get("name") == old_fk_field_name, old_fk_fields) @@ -257,7 +256,26 @@ class Migrate: new_data_field = next( filter(lambda x: x.get("name") == field_name, new_data_fields) ) - + changes = diff(old_data_field, new_data_field) + for change in changes: + _, 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: + cls._add_operator( + cls._add_index( + model, (field_name,), unique + ), + upgrade, + ) + else: + cls._add_operator( + cls._drop_index( + model, (field_name,), unique + ), + upgrade, + ) for old_model in old_models: if old_model not in new_models.keys(): cls._add_operator(cls.remove_model(cls._get_model(old_model)), upgrade) @@ -285,7 +303,7 @@ class Migrate: return ret @classmethod - def _remove_index(cls, model: Type[Model], fields_name: Tuple[str], unique=False): + def _drop_index(cls, model: Type[Model], fields_name: Tuple[str], unique=False): fields_name = cls._resolve_fk_fields_name(model, fields_name) return cls.ddl.drop_index(model, fields_name, unique) @@ -363,7 +381,8 @@ class Migrate: """ add fk :param model: - :param field: + :param field_describe: + :param reference_table_describe: :return: """ return cls.ddl.add_fk(model, field_describe, reference_table_describe) diff --git a/poetry.lock b/poetry.lock index 8233d6e..06bd9b4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -138,6 +138,20 @@ python-versions = "*" [package.dependencies] pyparsing = "*" +[[package]] +name = "dictdiffer" +version = "0.8.1" +description = "Dictdiffer is a library that helps you to diff and patch dictionaries." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +all = ["Sphinx (>=1.4.4)", "sphinx-rtd-theme (>=0.1.9)", "check-manifest (>=0.25)", "coverage (>=4.0)", "isort (>=4.2.2)", "mock (>=1.3.0)", "pydocstyle (>=1.0.0)", "pytest-cov (>=1.8.0)", "pytest-pep8 (>=1.0.6)", "pytest (>=2.8.0)", "tox (>=3.7.0)", "numpy (>=1.11.0)"] +docs = ["Sphinx (>=1.4.4)", "sphinx-rtd-theme (>=0.1.9)"] +numpy = ["numpy (>=1.11.0)"] +tests = ["check-manifest (>=0.25)", "coverage (>=4.0)", "isort (>=4.2.2)", "mock (>=1.3.0)", "pydocstyle (>=1.0.0)", "pytest-cov (>=1.8.0)", "pytest-pep8 (>=1.0.6)", "pytest (>=2.8.0)", "tox (>=3.7.0)"] + [[package]] name = "execnet" version = "1.8.0" @@ -554,7 +568,7 @@ dbdrivers = ["aiomysql", "asyncpg"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "57603697a31bfe9829bf3706b607c62edb7a3c1b18f45db6752e2d2261f0db41" +content-hash = "0f0150b05d3c48af70130c766a73bf4dff8091133186479ed51270789d277ce8" [metadata.files] aiomysql = [ @@ -621,11 +635,16 @@ click = [ ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] ddlparse = [ {file = "ddlparse-1.9.0-py3-none-any.whl", hash = "sha256:a7962615a9325be7d0f182cbe34011e6283996473fb98c784c6f675b9783bc18"}, {file = "ddlparse-1.9.0.tar.gz", hash = "sha256:cdffcf2f692f304a23c8e903b00afd7e83a920b79a2ff4e2f25c875b369d4f58"}, ] +dictdiffer = [ + {file = "dictdiffer-0.8.1-py2.py3-none-any.whl", hash = "sha256:d79d9a39e459fe33497c858470ca0d2e93cb96621751de06d631856adfd9c390"}, + {file = "dictdiffer-0.8.1.tar.gz", hash = "sha256:1adec0d67cdf6166bda96ae2934ddb5e54433998ceab63c984574d187cc563d2"}, +] execnet = [ {file = "execnet-1.8.0-py2.py3-none-any.whl", hash = "sha256:7a13113028b1e1cc4c6492b28098b3c6576c9dccc7973bfe47b342afadafb2ac"}, {file = "execnet-1.8.0.tar.gz", hash = "sha256:b73c5565e517f24b62dea8a5ceac178c661c4309d3aa0c3e420856c072c411b4"}, @@ -647,6 +666,7 @@ importlib-metadata = [ {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, ] iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] iso8601 = [ diff --git a/pyproject.toml b/pyproject.toml index 25fcf0c..1cab1ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ pydantic = "*" aiomysql = { version = "*", optional = true } asyncpg = { version = "*", optional = true } ddlparse = "*" +dictdiffer = "*" [tool.poetry.dev-dependencies] flake8 = "*" diff --git a/tests/test_migrate.py b/tests/test_migrate.py index f6ff6b9..23fa838 100644 --- a/tests/test_migrate.py +++ b/tests/test_migrate.py @@ -22,7 +22,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -38,7 +38,7 @@ old_models_describe = { "db_column": "slug", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -53,7 +53,7 @@ old_models_describe = { "db_column": "name", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -68,7 +68,7 @@ old_models_describe = { "db_column": "created_at", "python_type": "datetime.datetime", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -87,7 +87,7 @@ old_models_describe = { "db_column": "user_id", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -103,7 +103,7 @@ old_models_describe = { "field_type": "ForeignKeyFieldInstance", "python_type": "models.User", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -123,7 +123,7 @@ old_models_describe = { "field_type": "ManyToManyFieldInstance", "python_type": "models.Product", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -147,7 +147,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -163,7 +163,7 @@ old_models_describe = { "db_column": "label", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -178,7 +178,7 @@ old_models_describe = { "db_column": "key", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -193,7 +193,7 @@ old_models_describe = { "db_column": "value", "python_type": "Union[dict, list]", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -208,7 +208,7 @@ old_models_describe = { "db_column": "status", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": 1, @@ -238,7 +238,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -254,7 +254,7 @@ old_models_describe = { "db_column": "email", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -269,7 +269,7 @@ old_models_describe = { "db_column": "is_primary", "python_type": "bool", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": False, @@ -284,7 +284,7 @@ old_models_describe = { "db_column": "user_id", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -300,7 +300,7 @@ old_models_describe = { "field_type": "ForeignKeyFieldInstance", "python_type": "models.User", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -330,7 +330,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -346,7 +346,7 @@ old_models_describe = { "db_column": "name", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -361,7 +361,7 @@ old_models_describe = { "db_column": "view_num", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -376,7 +376,7 @@ old_models_describe = { "db_column": "sort", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -391,7 +391,7 @@ old_models_describe = { "db_column": "is_reviewed", "python_type": "bool", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -406,7 +406,7 @@ old_models_describe = { "db_column": "type", "python_type": "int", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -421,7 +421,7 @@ old_models_describe = { "db_column": "image", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -436,7 +436,7 @@ old_models_describe = { "db_column": "body", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -451,7 +451,7 @@ old_models_describe = { "db_column": "created_at", "python_type": "datetime.datetime", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -475,7 +475,7 @@ old_models_describe = { "field_type": "ManyToManyFieldInstance", "python_type": "models.Category", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -499,7 +499,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -515,9 +515,9 @@ old_models_describe = { "db_column": "username", "python_type": "str", "generated": False, - "Noneable": False, - "unique": True, - "indexed": True, + "nullable": False, + "unique": False, + "indexed": False, "default": None, "description": None, "docstring": None, @@ -530,7 +530,7 @@ old_models_describe = { "db_column": "password", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -545,7 +545,7 @@ old_models_describe = { "db_column": "last_login", "python_type": "datetime.datetime", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": "", @@ -564,7 +564,7 @@ old_models_describe = { "db_column": "is_active", "python_type": "bool", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": True, @@ -579,7 +579,7 @@ old_models_describe = { "db_column": "is_superuser", "python_type": "bool", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": False, @@ -594,7 +594,7 @@ old_models_describe = { "db_column": "avatar", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": "", @@ -609,7 +609,7 @@ old_models_describe = { "db_column": "intro", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": "", @@ -626,7 +626,7 @@ old_models_describe = { "field_type": "BackwardFKRelation", "python_type": "models.Category", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -639,7 +639,7 @@ old_models_describe = { "field_type": "BackwardFKRelation", "python_type": "models.Email", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -666,7 +666,7 @@ old_models_describe = { "db_column": "id", "python_type": "int", "generated": True, - "Noneable": False, + "nullable": False, "unique": True, "indexed": True, "default": None, @@ -682,7 +682,7 @@ old_models_describe = { "db_column": "version", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -697,7 +697,7 @@ old_models_describe = { "db_column": "app", "python_type": "str", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None, @@ -712,7 +712,7 @@ old_models_describe = { "db_column": "content", "python_type": "Union[dict, list]", "generated": False, - "Noneable": False, + "nullable": False, "unique": False, "indexed": False, "default": None,