Skip to content

Commit 91adf93

Browse files
authored
feat: support skip table migration by set managed=False (#397)
1 parent 41df464 commit 91adf93

File tree

7 files changed

+84
-10
lines changed

7 files changed

+84
-10
lines changed

.github/workflows/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ jobs:
2626
- tortoise022
2727
- tortoise023
2828
- tortoise024
29+
# TODO: add dev back when drop python3.8 support
30+
# - tortoisedev
2931
steps:
3032
- name: Start MySQL
3133
run: sudo systemctl start mysql.service

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#### Added
88
- feat: support command `python -m aerich`. ([#417])
99
- feat: add --fake to upgrade/downgrade. ([#398])
10+
- Support ignore table by settings `managed=False` in `Meta` class. ([#397])
1011

1112
#### Fixed
1213
- fix: aerich migrate raises tortoise.exceptions.FieldError when `index.INDEX_TYPE` is not empty. ([#415])
@@ -17,6 +18,7 @@
1718
### Changed
1819
- Refactored version management to use `importlib.metadata.version(__package__)` instead of hardcoded version string ([#412])
1920

21+
[#397]: https://github.com/tortoise/aerich/pull/397
2022
[#398]: https://github.com/tortoise/aerich/pull/398
2123
[#401]: https://github.com/tortoise/aerich/pull/401
2224
[#404]: https://github.com/tortoise/aerich/pull/404

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,16 @@ aerich downgrade --fake -v 2
295295
aerich --app models downgrade --fake -v 2
296296
```
297297

298+
### Ignore tables
299+
300+
You can tell aerich to ignore table by setting `managed=False` in the `Meta` class, e.g.:
301+
```py
302+
class MyModel(Model):
303+
class Meta:
304+
managed = False
305+
```
306+
**Note** `managed=False` does not recognized by `tortoise-orm` and `aerich init-db`, it is only for `aerich migrate`.
307+
298308
## License
299309

300310
This project is licensed under the

aerich/migrate.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@ def _handle_m2m_fields(
264264
) -> None:
265265
old_m2m_fields = cast("list[dict]", old_model_describe.get("m2m_fields", []))
266266
new_m2m_fields = cast("list[dict]", new_model_describe.get("m2m_fields", []))
267-
new_tables: dict[str, dict] = {field["table"]: field for field in new_models.values()}
267+
new_tables: dict[str, dict] = {
268+
field["table"]: field
269+
for field in new_models.values()
270+
if field.get("managed") is not False
271+
}
268272
for action, option, change in get_dict_diff_by_key(old_m2m_fields, new_m2m_fields):
269273
if (option and option[-1] == "nullable") or change[0][0] == "db_constraint":
270274
continue
@@ -387,6 +391,8 @@ def diff_models(
387391
models_with_rename_field: set[str] = set() # models that trigger the click.prompt
388392

389393
for new_model_str, new_model_describe in new_models.items():
394+
if upgrade and new_model_describe.get("managed") is False:
395+
continue
390396
model = cls._get_model(new_model_describe["name"].split(".")[1])
391397
if new_model_str not in old_models:
392398
if upgrade:
@@ -397,6 +403,8 @@ def diff_models(
397403
pass
398404
else:
399405
old_model_describe = cast(dict, old_models.get(new_model_str))
406+
if not upgrade and old_model_describe.get("managed") is False:
407+
continue
400408
# rename table
401409
new_table = cast(str, new_model_describe.get("table"))
402410
old_table = cast(str, old_model_describe.get("table"))
@@ -593,6 +601,8 @@ def diff_models(
593601
)
594602

595603
for old_model in old_models.keys() - new_models.keys():
604+
if not upgrade and old_models[old_model].get("managed") is False:
605+
continue
596606
cls._add_operator(cls.drop_model(old_models[old_model]["table"]), upgrade)
597607

598608
@classmethod

aerich/utils.py

+6-9
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,11 @@ def get_app_connection_name(config, app_name: str) -> str:
3434
get connection name
3535
:param config:
3636
:param app_name:
37-
:return:
37+
:return: the default connection name (Usally it is 'default')
3838
"""
39-
app = config.get("apps").get(app_name)
40-
if not app:
41-
raise BadOptionUsage(
42-
option_name="--app",
43-
message=f'Can\'t get app named "{app_name}"',
44-
)
45-
return app.get("default_connection", "default")
39+
if app := config.get("apps").get(app_name):
40+
return app.get("default_connection", "default")
41+
raise BadOptionUsage(option_name="--app", message=f"Can't get app named {app_name!r}")
4642

4743

4844
def get_app_connection(config, app) -> BaseDBAsyncClient:
@@ -89,8 +85,9 @@ def get_models_describe(app: str) -> dict:
8985
"""
9086
ret = {}
9187
for model in Tortoise.apps[app].values():
88+
managed = getattr(model.Meta, "managed", None)
9289
describe = model.describe()
93-
ret[describe.get("name")] = describe
90+
ret[describe.get("name")] = dict(describe, managed=managed)
9491
return ret
9592

9693

tests/models.py

+16
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class Product(Model):
102102
class Meta:
103103
unique_together = (("name", "type"),)
104104
indexes = (("name", "type"),)
105+
managed = True
105106

106107

107108
class Config(Model):
@@ -118,6 +119,21 @@ class Config(Model):
118119

119120
email: fields.OneToOneRelation[Email]
120121

122+
class Meta:
123+
managed = True
124+
125+
126+
class DontManageMe(Model):
127+
name = fields.CharField(max_length=50)
128+
129+
class Meta:
130+
managed = False
131+
132+
133+
class Ignore(Model):
134+
class Meta:
135+
managed = False
136+
121137

122138
class NewModel(Model):
123139
name = fields.CharField(max_length=50)

tests/old_models.py

+37
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,40 @@ class Config(Model):
8989

9090
class Meta:
9191
table = "configs"
92+
93+
94+
class DontManageMe(Model):
95+
name = fields.CharField(max_length=50)
96+
97+
class Meta:
98+
table = "dont_manage"
99+
100+
101+
class Ignore(Model):
102+
name = fields.CharField(max_length=50)
103+
104+
class Meta:
105+
managed = True
106+
107+
108+
def main() -> None:
109+
"""Generate a python file for the old_models_describe"""
110+
from pathlib import Path
111+
112+
from tortoise import run_async
113+
from tortoise.contrib.test import init_memory_sqlite
114+
115+
from aerich.utils import get_models_describe
116+
117+
@init_memory_sqlite
118+
async def run() -> None:
119+
old_models_describe = get_models_describe("models")
120+
p = Path("old_models_describe.py")
121+
p.write_text(f"{old_models_describe = }", encoding="utf-8")
122+
print(f"Write value to {p}\nYou can reformat it by `ruff format {p}`")
123+
124+
run_async(run())
125+
126+
127+
if __name__ == "__main__":
128+
main()

0 commit comments

Comments
 (0)