fix: add o2o field does not create constraint when migrating (#396)
* fix: add o2o field does not create constraint when migrating * Add testcase and update changelog * docs: update migrating list * refactor: use `_handle_o2o_fields` instead of `is_o2o=True` * Remove unused line
This commit is contained in:
parent
1acb9ed1e7
commit
f93faa8afb
@ -5,6 +5,7 @@
|
||||
### [0.8.1](Unreleased)
|
||||
|
||||
#### Fixed
|
||||
- fix: add o2o field does not create constraint when migrating. (#396)
|
||||
- fix: intermediate table for m2m relation not created. (#394)
|
||||
- Migrate add m2m field with custom through generate duplicated table. (#393)
|
||||
- Migrate drop the wrong m2m field when model have multi m2m fields. (#376)
|
||||
|
@ -281,6 +281,68 @@ class Migrate:
|
||||
if add:
|
||||
cls._add_operator(cls.drop_m2m(table), upgrade, True)
|
||||
|
||||
@classmethod
|
||||
def _handle_relational(
|
||||
cls,
|
||||
key: str,
|
||||
old_model_describe: Dict,
|
||||
new_model_describe: Dict,
|
||||
model: Type[Model],
|
||||
old_models: Dict,
|
||||
new_models: Dict,
|
||||
upgrade=True,
|
||||
) -> None:
|
||||
old_fk_fields = cast(List[dict], old_model_describe.get(key))
|
||||
new_fk_fields = cast(List[dict], new_model_describe.get(key))
|
||||
|
||||
old_fk_fields_name: List[str] = [i.get("name", "") for i in old_fk_fields]
|
||||
new_fk_fields_name: List[str] = [i.get("name", "") for i in new_fk_fields]
|
||||
|
||||
# add
|
||||
for new_fk_field_name in set(new_fk_fields_name).difference(set(old_fk_fields_name)):
|
||||
fk_field = cls.get_field_by_name(new_fk_field_name, new_fk_fields)
|
||||
if fk_field.get("db_constraint"):
|
||||
ref_describe = cast(dict, new_models[fk_field["python_type"]])
|
||||
sql = cls._add_fk(model, fk_field, ref_describe)
|
||||
cls._add_operator(sql, upgrade, fk_m2m_index=True)
|
||||
# drop
|
||||
for old_fk_field_name in set(old_fk_fields_name).difference(set(new_fk_fields_name)):
|
||||
old_fk_field = cls.get_field_by_name(old_fk_field_name, cast(List[dict], old_fk_fields))
|
||||
if old_fk_field.get("db_constraint"):
|
||||
ref_describe = cast(dict, old_models[old_fk_field["python_type"]])
|
||||
sql = cls._drop_fk(model, old_fk_field, ref_describe)
|
||||
cls._add_operator(sql, upgrade, fk_m2m_index=True)
|
||||
|
||||
@classmethod
|
||||
def _handle_fk_fields(
|
||||
cls,
|
||||
old_model_describe: Dict,
|
||||
new_model_describe: Dict,
|
||||
model: Type[Model],
|
||||
old_models: Dict,
|
||||
new_models: Dict,
|
||||
upgrade=True,
|
||||
) -> None:
|
||||
key = "fk_fields"
|
||||
cls._handle_relational(
|
||||
key, old_model_describe, new_model_describe, model, old_models, new_models, upgrade
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _handle_o2o_fields(
|
||||
cls,
|
||||
old_model_describe: Dict,
|
||||
new_model_describe: Dict,
|
||||
model: Type[Model],
|
||||
old_models: Dict,
|
||||
new_models: Dict,
|
||||
upgrade=True,
|
||||
) -> None:
|
||||
key = "o2o_fields"
|
||||
cls._handle_relational(
|
||||
key, old_model_describe, new_model_describe, model, old_models, new_models, upgrade
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def diff_models(
|
||||
cls, old_models: Dict[str, dict], new_models: Dict[str, dict], upgrade=True
|
||||
@ -334,6 +396,13 @@ class Migrate:
|
||||
# current only support rename pk
|
||||
if action == "change" and option == "name":
|
||||
cls._add_operator(cls._rename_field(model, *change), upgrade)
|
||||
# fk fields
|
||||
args = (old_model_describe, new_model_describe, model, old_models, new_models)
|
||||
cls._handle_fk_fields(*args, upgrade=upgrade)
|
||||
# o2o fields
|
||||
cls._handle_o2o_fields(*args, upgrade=upgrade)
|
||||
old_o2o_columns = [i["raw_field"] for i in old_model_describe.get("o2o_fields", [])]
|
||||
new_o2o_columns = [i["raw_field"] for i in new_model_describe.get("o2o_fields", [])]
|
||||
# m2m fields
|
||||
cls._handle_m2m_fields(
|
||||
old_model_describe, new_model_describe, model, new_models, upgrade
|
||||
@ -424,7 +493,10 @@ class Migrate:
|
||||
),
|
||||
upgrade,
|
||||
)
|
||||
if new_data_field["indexed"]:
|
||||
if (
|
||||
new_data_field["indexed"]
|
||||
and new_data_field["db_column"] not in new_o2o_columns
|
||||
):
|
||||
cls._add_operator(
|
||||
cls._add_index(
|
||||
model, (new_data_field["db_column"],), new_data_field["unique"]
|
||||
@ -447,7 +519,10 @@ class Migrate:
|
||||
cls._remove_field(model, db_column),
|
||||
upgrade,
|
||||
)
|
||||
if old_data_field["indexed"]:
|
||||
if (
|
||||
old_data_field["indexed"]
|
||||
and old_data_field["db_column"] not in old_o2o_columns
|
||||
):
|
||||
is_unique_field = old_data_field.get("unique")
|
||||
cls._add_operator(
|
||||
cls._drop_index(model, {db_column}, is_unique_field),
|
||||
@ -455,38 +530,6 @@ class Migrate:
|
||||
True,
|
||||
)
|
||||
|
||||
old_fk_fields = cast(List[dict], old_model_describe.get("fk_fields"))
|
||||
new_fk_fields = cast(List[dict], new_model_describe.get("fk_fields"))
|
||||
|
||||
old_fk_fields_name: List[str] = [i.get("name", "") for i in old_fk_fields]
|
||||
new_fk_fields_name: List[str] = [i.get("name", "") for i in new_fk_fields]
|
||||
|
||||
# add fk
|
||||
for new_fk_field_name in set(new_fk_fields_name).difference(
|
||||
set(old_fk_fields_name)
|
||||
):
|
||||
fk_field = cls.get_field_by_name(new_fk_field_name, new_fk_fields)
|
||||
if fk_field.get("db_constraint"):
|
||||
ref_describe = cast(dict, new_models[fk_field["python_type"]])
|
||||
cls._add_operator(
|
||||
cls._add_fk(model, fk_field, ref_describe),
|
||||
upgrade,
|
||||
fk_m2m_index=True,
|
||||
)
|
||||
# drop fk
|
||||
for old_fk_field_name in set(old_fk_fields_name).difference(
|
||||
set(new_fk_fields_name)
|
||||
):
|
||||
old_fk_field = cls.get_field_by_name(
|
||||
old_fk_field_name, cast(List[dict], old_fk_fields)
|
||||
)
|
||||
if old_fk_field.get("db_constraint"):
|
||||
ref_describe = cast(dict, old_models[old_fk_field["python_type"]])
|
||||
cls._add_operator(
|
||||
cls._drop_fk(model, old_fk_field, ref_describe),
|
||||
upgrade,
|
||||
fk_m2m_index=True,
|
||||
)
|
||||
# change fields
|
||||
for field_name in set(new_data_fields_name).intersection(set(old_data_fields_name)):
|
||||
old_data_field = cls.get_field_by_name(field_name, old_data_fields)
|
||||
|
@ -40,6 +40,7 @@ class Email(Model):
|
||||
is_primary = fields.BooleanField(default=False)
|
||||
address = fields.CharField(max_length=200)
|
||||
users: fields.ManyToManyRelation[User] = fields.ManyToManyField("models.User")
|
||||
config: fields.OneToOneRelation["Config"] = fields.OneToOneField("models.Config")
|
||||
|
||||
|
||||
def default_name():
|
||||
@ -91,6 +92,8 @@ class Config(Model):
|
||||
"models.User", description="User"
|
||||
)
|
||||
|
||||
email: fields.OneToOneRelation["Email"]
|
||||
|
||||
|
||||
class NewModel(Model):
|
||||
name = fields.CharField(max_length=50)
|
||||
|
@ -875,6 +875,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
- drop field: User.avatar
|
||||
- add index: Email.email
|
||||
- add many to many: Email.users
|
||||
- add one to one: Email.config
|
||||
- remove unique: Category.title
|
||||
- add unique: User.username
|
||||
- change column: length User.password
|
||||
@ -914,6 +915,8 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `config` ALTER COLUMN `status` DROP DEFAULT",
|
||||
"ALTER TABLE `config` MODIFY COLUMN `value` JSON NOT NULL",
|
||||
"ALTER TABLE `email` ADD `address` VARCHAR(200) NOT NULL",
|
||||
"ALTER TABLE `email` ADD CONSTRAINT `fk_email_config_76a9dc71` FOREIGN KEY (`config_id`) REFERENCES `config` (`id`) ON DELETE CASCADE",
|
||||
"ALTER TABLE `email` ADD `config_id` INT NOT NULL UNIQUE",
|
||||
"ALTER TABLE `configs` RENAME TO `config`",
|
||||
"ALTER TABLE `product` DROP COLUMN `uuid`",
|
||||
"ALTER TABLE `product` DROP INDEX `uuid`",
|
||||
@ -954,6 +957,8 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `config` ALTER COLUMN `status` SET DEFAULT 1",
|
||||
"ALTER TABLE `email` ADD `user_id` INT NOT NULL",
|
||||
"ALTER TABLE `email` DROP COLUMN `address`",
|
||||
"ALTER TABLE `email` DROP COLUMN `config_id`",
|
||||
"ALTER TABLE `email` DROP FOREIGN KEY `fk_email_config_76a9dc71`",
|
||||
"ALTER TABLE `config` RENAME TO `configs`",
|
||||
"ALTER TABLE `product` RENAME COLUMN `pic` TO `image`",
|
||||
"ALTER TABLE `email` RENAME COLUMN `email_id` TO `id`",
|
||||
@ -1007,6 +1012,8 @@ 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',
|
||||
'ALTER TABLE "email" ADD CONSTRAINT "fk_email_config_76a9dc71" FOREIGN KEY ("config_id") REFERENCES "config" ("id") ON DELETE CASCADE',
|
||||
'ALTER TABLE "email" ADD "config_id" INT NOT NULL UNIQUE',
|
||||
'DROP INDEX IF EXISTS "uid_product_uuid_d33c18"',
|
||||
'ALTER TABLE "product" DROP COLUMN "uuid"',
|
||||
'ALTER TABLE "product" ALTER COLUMN "view_num" SET DEFAULT 0',
|
||||
@ -1048,6 +1055,8 @@ def test_migrate(mocker: MockerFixture):
|
||||
'ALTER TABLE "email" DROP COLUMN "address"',
|
||||
'ALTER TABLE "email" RENAME COLUMN "email_id" TO "id"',
|
||||
'ALTER TABLE "email" ALTER COLUMN "is_primary" TYPE BOOL USING "is_primary"::BOOL',
|
||||
'ALTER TABLE "email" DROP COLUMN "config_id"',
|
||||
'ALTER TABLE "email" DROP CONSTRAINT IF EXISTS "fk_email_config_76a9dc71"',
|
||||
'ALTER TABLE "product" ADD "uuid" INT NOT NULL UNIQUE',
|
||||
'CREATE UNIQUE INDEX "uid_product_uuid_d33c18" ON "product" ("uuid")',
|
||||
'ALTER TABLE "product" ALTER COLUMN "view_num" DROP DEFAULT',
|
||||
|
Loading…
x
Reference in New Issue
Block a user