fix: inspectdb not match data type 'DOUBLE' and 'CHAR' for MySQL
* increase: 1. Inspectdb adds DECIMAL, DOUBLE, CHAR, TIME data type matching; 2. Add exception handling, avoid the need to manually create the entire table because a certain data type is not supported. * fix: aerich inspectdb raise KeyError for double in MySQL * feat: support command `python -m aerich` * docs: update changelog * tests: verify mysql inspectdb for float field * fix mysql uuid field inspect to be charfield * refactor: use `db_index=True` instead of `index=True` for inspectdb * docs: update changelog --------- Co-authored-by: xiechen <xiechen@jinse.com> Co-authored-by: Waket Zheng <waketzheng@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import contextlib
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -72,7 +73,12 @@ class Dialect:
|
||||
return not cls.test_db_url or "sqlite" in cls.test_db_url
|
||||
|
||||
|
||||
WINDOWS = platform.system() == "Windows"
|
||||
|
||||
|
||||
def run_shell(command: str, capture_output=True, **kw) -> str:
|
||||
if WINDOWS and command.startswith("aerich "):
|
||||
command = "python -m " + command
|
||||
r = subprocess.run(shlex.split(command), capture_output=capture_output)
|
||||
if r.returncode != 0 and r.stderr:
|
||||
return r.stderr.decode()
|
||||
|
||||
@@ -93,6 +93,8 @@ class Product(Model):
|
||||
)
|
||||
pic = fields.CharField(max_length=200)
|
||||
body = fields.TextField()
|
||||
price = fields.FloatField(null=True)
|
||||
no = fields.UUIDField(db_index=True)
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
is_deleted = fields.BooleanField(default=False)
|
||||
|
||||
|
||||
@@ -2,41 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from aerich.ddl.sqlite import SqliteDDL
|
||||
from aerich.migrate import Migrate
|
||||
from tests._utils import chdir, copy_files, run_shell
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def new_aerich_project(tmp_path: Path):
|
||||
test_dir = Path(__file__).parent
|
||||
asset_dir = test_dir / "assets" / "fake"
|
||||
settings_py = asset_dir / "settings.py"
|
||||
_tests_py = asset_dir / "_tests.py"
|
||||
db_py = asset_dir / "db.py"
|
||||
models_py = test_dir / "models.py"
|
||||
models_second_py = test_dir / "models_second.py"
|
||||
copy_files(settings_py, _tests_py, models_py, models_second_py, db_py, target_dir=tmp_path)
|
||||
dst_dir = tmp_path / "tests"
|
||||
dst_dir.mkdir()
|
||||
dst_dir.joinpath("__init__.py").touch()
|
||||
copy_files(test_dir / "_utils.py", test_dir / "indexes.py", target_dir=dst_dir)
|
||||
if should_remove := str(tmp_path) not in sys.path:
|
||||
sys.path.append(str(tmp_path))
|
||||
with chdir(tmp_path):
|
||||
run_shell("python db.py create", capture_output=False)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if not os.getenv("AERICH_DONT_DROP_FAKE_DB"):
|
||||
run_shell("python db.py drop", capture_output=False)
|
||||
if should_remove:
|
||||
sys.path.remove(str(tmp_path))
|
||||
from tests._utils import Dialect, run_shell
|
||||
|
||||
|
||||
def _append_field(*files: str, name="field_1") -> None:
|
||||
@@ -48,7 +16,7 @@ def _append_field(*files: str, name="field_1") -> None:
|
||||
|
||||
|
||||
def test_fake(new_aerich_project):
|
||||
if (ddl := getattr(Migrate, "ddl", None)) and isinstance(ddl, SqliteDDL):
|
||||
if Dialect.is_sqlite():
|
||||
# TODO: go ahead if sqlite alter-column supported
|
||||
return
|
||||
output = run_shell("aerich init -t settings.TORTOISE_ORM")
|
||||
|
||||
17
tests/test_inspectdb.py
Normal file
17
tests/test_inspectdb.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from tests._utils import Dialect, run_shell
|
||||
|
||||
|
||||
def test_inspect(new_aerich_project):
|
||||
if Dialect.is_sqlite():
|
||||
# TODO: test sqlite after #384 fixed
|
||||
return
|
||||
run_shell("aerich init -t settings.TORTOISE_ORM")
|
||||
run_shell("aerich init-db")
|
||||
ret = run_shell("aerich inspectdb -t product")
|
||||
assert ret.startswith("from tortoise import Model, fields")
|
||||
assert "primary_key=True" in ret
|
||||
assert "fields.DatetimeField" in ret
|
||||
assert "fields.FloatField" in ret
|
||||
assert "fields.UUIDField" in ret
|
||||
if Dialect.is_mysql():
|
||||
assert "db_index=True" in ret
|
||||
@@ -981,8 +981,11 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `product` DROP COLUMN `uuid`",
|
||||
"ALTER TABLE `product` DROP INDEX `uuid`",
|
||||
"ALTER TABLE `product` RENAME COLUMN `image` TO `pic`",
|
||||
"ALTER TABLE `product` ADD `price` DOUBLE",
|
||||
"ALTER TABLE `product` ADD `no` CHAR(36) NOT NULL",
|
||||
"ALTER TABLE `email` RENAME COLUMN `id` TO `email_id`",
|
||||
"ALTER TABLE `product` ADD INDEX `idx_product_name_869427` (`name`, `type_db_alias`)",
|
||||
"ALTER TABLE `product` ADD INDEX `idx_product_no_e4d701` (`no`)",
|
||||
"ALTER TABLE `email` ADD INDEX `idx_email_email_4a1a33` (`email`)",
|
||||
"ALTER TABLE `product` ADD UNIQUE INDEX `uid_product_name_869427` (`name`, `type_db_alias`)",
|
||||
"ALTER TABLE `product` ALTER COLUMN `view_num` SET DEFAULT 0",
|
||||
@@ -1027,8 +1030,11 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `product` ADD `uuid` INT NOT NULL UNIQUE",
|
||||
"ALTER TABLE `product` ADD UNIQUE INDEX `uuid` (`uuid`)",
|
||||
"ALTER TABLE `product` DROP INDEX `idx_product_name_869427`",
|
||||
"ALTER TABLE `product` DROP COLUMN `price`",
|
||||
"ALTER TABLE `product` DROP COLUMN `no`",
|
||||
"ALTER TABLE `email` DROP INDEX `idx_email_email_4a1a33`",
|
||||
"ALTER TABLE `product` DROP INDEX `uid_product_name_869427`",
|
||||
"ALTER TABLE `product` DROP INDEX `idx_product_no_e4d701`",
|
||||
"ALTER TABLE `product` ALTER COLUMN `view_num` DROP DEFAULT",
|
||||
"ALTER TABLE `product` RENAME COLUMN `is_deleted` TO `is_delete`",
|
||||
"ALTER TABLE `product` RENAME COLUMN `is_reviewed` TO `is_review`",
|
||||
@@ -1074,11 +1080,14 @@ def test_migrate(mocker: MockerFixture):
|
||||
'ALTER TABLE "product" RENAME COLUMN "image" TO "pic"',
|
||||
'ALTER TABLE "product" RENAME COLUMN "is_review" TO "is_reviewed"',
|
||||
'ALTER TABLE "product" RENAME COLUMN "is_delete" TO "is_deleted"',
|
||||
'ALTER TABLE "product" ADD "price" DOUBLE PRECISION',
|
||||
'ALTER TABLE "product" ADD "no" UUID NOT NULL',
|
||||
'ALTER TABLE "user" ALTER COLUMN "password" TYPE VARCHAR(100) USING "password"::VARCHAR(100)',
|
||||
'ALTER TABLE "user" DROP COLUMN "avatar"',
|
||||
'ALTER TABLE "user" ALTER COLUMN "longitude" TYPE DECIMAL(10,8) USING "longitude"::DECIMAL(10,8)',
|
||||
'CREATE INDEX IF NOT EXISTS "idx_product_name_869427" ON "product" ("name", "type_db_alias")',
|
||||
'CREATE INDEX IF NOT EXISTS "idx_email_email_4a1a33" ON "email" ("email")',
|
||||
'CREATE INDEX IF NOT EXISTS "idx_product_no_e4d701" ON "product" ("no")',
|
||||
'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)',
|
||||
'CREATE TABLE IF NOT EXISTS "newmodel" (\n "id" SERIAL NOT NULL PRIMARY KEY,\n "name" VARCHAR(50) NOT NULL\n);\nCOMMENT ON COLUMN "config"."user_id" IS \'User\'',
|
||||
'CREATE UNIQUE INDEX IF NOT EXISTS "uid_product_name_869427" ON "product" ("name", "type_db_alias")',
|
||||
@@ -1118,6 +1127,8 @@ def test_migrate(mocker: MockerFixture):
|
||||
'ALTER TABLE "product" RENAME COLUMN "pic" TO "image"',
|
||||
'ALTER TABLE "product" RENAME COLUMN "is_deleted" TO "is_delete"',
|
||||
'ALTER TABLE "product" RENAME COLUMN "is_reviewed" TO "is_review"',
|
||||
'ALTER TABLE "product" DROP COLUMN "price"',
|
||||
'ALTER TABLE "product" DROP COLUMN "no"',
|
||||
'ALTER TABLE "user" ADD "avatar" VARCHAR(200) NOT NULL DEFAULT \'\'',
|
||||
'ALTER TABLE "user" ALTER COLUMN "password" TYPE VARCHAR(200) USING "password"::VARCHAR(200)',
|
||||
'ALTER TABLE "user" ALTER COLUMN "longitude" TYPE DECIMAL(12,9) USING "longitude"::DECIMAL(12,9)',
|
||||
@@ -1126,6 +1137,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
'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 INDEX IF EXISTS "idx_product_no_e4d701"',
|
||||
'DROP TABLE IF EXISTS "email_user"',
|
||||
'DROP TABLE IF EXISTS "newmodel"',
|
||||
'CREATE TABLE "config_category" (\n "config_id" INT NOT NULL REFERENCES "config" ("id") ON DELETE CASCADE,\n "category_id" INT NOT NULL REFERENCES "category" ("id") ON DELETE CASCADE\n)',
|
||||
|
||||
Reference in New Issue
Block a user