12 Commits

Author SHA1 Message Date
long2ice
63e8d06157 remove aiomysql 2021-12-08 14:43:33 +08:00
long2ice
68ef8ac676 Fix ci 2021-12-08 14:38:16 +08:00
long2ice
8b5cf6faa0 inspectdb support DATE. (#215) 2021-12-08 14:33:27 +08:00
long2ice
fac00d45cc Remove pydantic dependency. (#198) 2021-10-04 23:05:20 +08:00
long2ice
6f7893d376 Fix section name 2021-09-28 15:07:10 +08:00
long2ice
b1521c4cc7 update version 2021-09-27 19:55:38 +08:00
long2ice
24c1f4cb7d Change default config file from aerich.ini to pyproject.toml. (#197) 2021-09-27 11:05:20 +08:00
long2ice
661f241dac Compatible with old version in indexes 2021-08-31 17:53:17 +08:00
long2ice
01787558d6 Fix test 2021-08-31 17:41:13 +08:00
long2ice
699b0321a4 Support indexes change. (#193) 2021-08-31 17:36:25 +08:00
long2ice
4a83021892 Update FUNDING.yml 2021-08-26 20:39:31 +08:00
long2ice
af63221875 Fix no module found error. (#188) (#189) 2021-08-16 11:14:43 +08:00
15 changed files with 422 additions and 458 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1 +1 @@
custom: ["https://sponsor.long2ice.cn"]
custom: ["https://sponsor.long2ice.io"]

View File

@@ -26,9 +26,9 @@ jobs:
with:
python-version: '3.x'
- name: Install and configure Poetry
uses: snok/install-poetry@v1.1.1
with:
virtualenvs-create: false
run: |
pip install -U pip poetry
poetry config virtualenvs.create false
- name: CI
env:
MYSQL_PASS: root

View File

@@ -12,9 +12,9 @@ jobs:
with:
python-version: '3.x'
- name: Install and configure Poetry
uses: snok/install-poetry@v1.1.1
with:
virtualenvs-create: false
run: |
pip install -U pip poetry
poetry config virtualenvs.create false
- name: Build dists
run: make build
- name: Pypi Publish

View File

@@ -1,7 +1,27 @@
# ChangeLog
## 0.6
### 0.6.0
- Change default config file from `aerich.ini` to `pyproject.toml`. (#197)
**Upgrade note:**
1. Run `aerich init -t config.TORTOISE_ORM`.
2. Remove `aerich.ini`.
- Remove `pydantic` dependency. (#198)
- `inspectdb` support `DATE`. (#215)
## 0.5
### 0.5.8
- Support `indexes` change. (#193)
### 0.5.7
- Fix no module found error. (#188) (#189)
### 0.5.6
- Add `Command` class. (#148) (#141) (#123) (#106)

View File

@@ -12,7 +12,7 @@ up:
@poetry update
deps:
@poetry install -E asyncpg -E asyncmy -E aiomysql
@poetry install -E asyncpg -E asyncmy
style: deps
isort -src $(checkfiles)

View File

@@ -15,7 +15,7 @@ it\'s own migration solution.
Just install from pypi:
```shell
> pip install aerich
pip install aerich
```
## Quick Start
@@ -27,11 +27,8 @@ Usage: aerich [OPTIONS] COMMAND [ARGS]...
Options:
-V, --version Show the version and exit.
-c, --config TEXT Config file. [default: aerich.ini]
-c, --config TEXT Config file. [default: pyproject.toml]
--app TEXT Tortoise-ORM app name.
-n, --name TEXT Name of section in .ini file to use for aerich config.
[default: aerich]
-h, --help Show this message and exit.
Commands:
@@ -70,10 +67,9 @@ Usage: aerich init [OPTIONS]
Init config file and generate root migrate location.
OOptions:
Options:
-t, --tortoise-orm TEXT Tortoise-ORM config module dict variable, like
settings.TORTOISE_ORM. [required]
--location TEXT Migrate store location. [default: ./migrations]
-s, --src_folder TEXT Folder of the source, relative to the project root.
-h, --help Show this message and exit.
@@ -85,7 +81,7 @@ Initialize the config file and migrations location:
> aerich init -t tests.backends.mysql.TORTOISE_ORM
Success create migrate location ./migrations
Success generate config file aerich.ini
Success write config to pyproject.toml
```
### Init db

View File

@@ -1,5 +1,3 @@
__version__ = "0.5.6"
import os
from pathlib import Path
from typing import List
@@ -14,7 +12,6 @@ from aerich.inspectdb import InspectDb
from aerich.migrate import Migrate
from aerich.models import Aerich
from aerich.utils import (
add_src_path,
get_app_connection,
get_app_connection_name,
get_models_describe,
@@ -29,14 +26,11 @@ class Command:
tortoise_config: dict,
app: str = "models",
location: str = "./migrations",
src_folder: str = ".",
):
self.tortoise_config = tortoise_config
self.app = app
self.location = location
self.src_folder = src_folder
Migrate.app = app
add_src_path(src_folder)
async def init(self):
await Migrate.init(self.tortoise_config, self.app, self.location)

View File

@@ -1,21 +1,21 @@
import asyncio
import os
from configparser import ConfigParser
from functools import wraps
from pathlib import Path
from typing import List
import click
import tomlkit
from click import Context, UsageError
from tomlkit.exceptions import NonExistentKey
from tortoise import Tortoise
from aerich.exceptions import DowngradeError
from aerich.utils import add_src_path, get_tortoise_config
from . import Command, __version__
from . import Command
from .enums import Color
parser = ConfigParser()
from .version import __version__
CONFIG_DEFAULT_VALUES = {
"src_folder": ".",
@@ -42,39 +42,35 @@ def coro(f):
@click.option(
"-c",
"--config",
default="aerich.ini",
default="pyproject.toml",
show_default=True,
help="Config file.",
)
@click.option("--app", required=False, help="Tortoise-ORM app name.")
@click.option(
"-n",
"--name",
default="aerich",
show_default=True,
help="Name of section in .ini file to use for aerich config.",
)
@click.pass_context
@coro
async def cli(ctx: Context, config, app, name):
async def cli(ctx: Context, config, app):
ctx.ensure_object(dict)
ctx.obj["config_file"] = config
ctx.obj["name"] = name
invoked_subcommand = ctx.invoked_subcommand
if invoked_subcommand != "init":
if not Path(config).exists():
raise UsageError("You must exec init first", ctx=ctx)
parser.read(config)
location = parser[name]["location"]
tortoise_orm = parser[name]["tortoise_orm"]
src_folder = parser[name].get("src_folder", CONFIG_DEFAULT_VALUES["src_folder"])
with open(config, "r") as f:
content = f.read()
doc = tomlkit.parse(content)
try:
tool = doc["tool"]["aerich"]
location = tool["location"]
tortoise_orm = tool["tortoise_orm"]
src_folder = tool.get("src_folder", CONFIG_DEFAULT_VALUES["src_folder"])
except NonExistentKey:
raise UsageError("You need run aerich init again when upgrade to 0.6.0+")
add_src_path(src_folder)
tortoise_config = get_tortoise_config(ctx, tortoise_orm)
app = app or list(tortoise_config.get("apps").keys())[0]
command = Command(
tortoise_config=tortoise_config, app=app, location=location, src_folder=src_folder
)
command = Command(tortoise_config=tortoise_config, app=app, location=location)
ctx.obj["command"] = command
if invoked_subcommand != "init-db":
if not Path(location, app).exists():
@@ -187,9 +183,6 @@ async def history(ctx: Context):
@coro
async def init(ctx: Context, tortoise_orm, location, src_folder):
config_file = ctx.obj["config_file"]
name = ctx.obj["name"]
if Path(config_file).exists():
return click.secho("Configuration file already created", fg=Color.yellow)
if os.path.isabs(src_folder):
src_folder = os.path.relpath(os.getcwd(), src_folder)
@@ -201,18 +194,22 @@ async def init(ctx: Context, tortoise_orm, location, src_folder):
add_src_path(src_folder)
get_tortoise_config(ctx, tortoise_orm)
parser.add_section(name)
parser.set(name, "tortoise_orm", tortoise_orm)
parser.set(name, "location", location)
parser.set(name, "src_folder", src_folder)
with open(config_file, "r") as f:
content = f.read()
doc = tomlkit.parse(content)
table = tomlkit.table()
table["tortoise_orm"] = tortoise_orm
table["location"] = location
table["src_folder"] = src_folder
doc["tool"]["aerich"] = table
with open(config_file, "w", encoding="utf-8") as f:
parser.write(f)
with open(config_file, "w") as f:
f.write(tomlkit.dumps(doc))
Path(location).mkdir(parents=True, exist_ok=True)
click.secho(f"Success create migrate location {location}", fg=Color.green)
click.secho(f"Success generate config file {config_file}", fg=Color.green)
click.secho(f"Success write config to {config_file}", fg=Color.green)
@cli.command(help="Generate schema and generate app migrate location.")

View File

@@ -16,6 +16,7 @@ class InspectDb:
"TEXT": " {field} = fields.TextField({null}{default}{comment})",
"DATETIME": " {field} = fields.DatetimeField({null}{default}{comment})",
"FLOAT": " {field} = fields.FloatField({null}{default}{comment})",
"DATE": " {field} = fields.DateField({null}{default}{comment})",
}
def __init__(self, conn: BaseDBAsyncClient, tables: Optional[List[str]] = None):

View File

@@ -138,21 +138,21 @@ class Migrate:
return await cls._generate_diff_sql(name)
@classmethod
def _add_operator(cls, operator: str, upgrade=True, fk_m2m=False):
def _add_operator(cls, operator: str, upgrade=True, fk_m2m_index=False):
"""
add operator,differentiate fk because fk is order limit
:param operator:
:param upgrade:
:param fk_m2m:
:param fk_m2m_index:
:return:
"""
if upgrade:
if fk_m2m:
if fk_m2m_index:
cls._upgrade_fk_m2m_index_operators.append(operator)
else:
cls.upgrade_operators.append(operator)
else:
if fk_m2m:
if fk_m2m_index:
cls._downgrade_fk_m2m_index_operators.append(operator)
else:
cls.downgrade_operators.append(operator)
@@ -192,7 +192,8 @@ class Migrate:
new_unique_together = set(
map(lambda x: tuple(x), new_model_describe.get("unique_together"))
)
old_indexes = set(map(lambda x: tuple(x), old_model_describe.get("indexes", [])))
new_indexes = set(map(lambda x: tuple(x), new_model_describe.get("indexes", [])))
old_pk_field = old_model_describe.get("pk_field")
new_pk_field = new_model_describe.get("pk_field")
# pk field
@@ -224,7 +225,7 @@ class Migrate:
new_models.get(change[0][1].get("model_name")),
),
upgrade,
fk_m2m=True,
fk_m2m_index=True,
)
elif action == "remove":
add = False
@@ -235,14 +236,19 @@ class Migrate:
cls._downgrade_m2m.append(table)
add = True
if add:
cls._add_operator(cls.drop_m2m(table), upgrade, fk_m2m=True)
cls._add_operator(cls.drop_m2m(table), upgrade, True)
# add unique_together
for index in new_unique_together.difference(old_unique_together):
cls._add_operator(cls._add_index(model, index, True), upgrade, True)
# remove unique_together
for index in old_unique_together.difference(new_unique_together):
cls._add_operator(cls._drop_index(model, index, True), upgrade, True)
# add indexes
for index in new_indexes.difference(old_indexes):
cls._add_operator(cls._add_index(model, index, False), upgrade, True)
# remove indexes
for index in old_indexes.difference(new_indexes):
cls._add_operator(cls._drop_index(model, index, False), upgrade, True)
old_data_fields = old_model_describe.get("data_fields")
new_data_fields = new_model_describe.get("data_fields")
@@ -356,7 +362,7 @@ class Migrate:
model, fk_field, new_models.get(fk_field.get("python_type"))
),
upgrade,
fk_m2m=True,
fk_m2m_index=True,
)
# drop fk
for old_fk_field_name in set(old_fk_fields_name).difference(
@@ -371,7 +377,7 @@ class Migrate:
model, old_fk_field, old_models.get(old_fk_field.get("python_type"))
),
upgrade,
fk_m2m=True,
fk_m2m_index=True,
)
# change fields
for field_name in set(new_data_fields_name).intersection(set(old_data_fields_name)):

1
aerich/version.py Normal file
View File

@@ -0,0 +1 @@
__version__ = "0.6.0"

717
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "aerich"
version = "0.5.6"
version = "0.6.0"
description = "A database migrations tool for Tortoise ORM."
authors = ["long2ice <long2ice@gmail.com>"]
license = "Apache-2.0"
@@ -16,13 +16,13 @@ include = ["CHANGELOG.md", "LICENSE", "README.md"]
[tool.poetry.dependencies]
python = "^3.7"
tortoise-orm = "*"
tortoise-orm = "^0.17.7"
click = "*"
pydantic = "*"
aiomysql = { version = "*", optional = true }
asyncpg = { version = "*", optional = true }
asyncmy = { version = "*", optional = true }
ddlparse = "*"
dictdiffer = "*"
tomlkit = "*"
[tool.poetry.dev-dependencies]
flake8 = "*"
@@ -38,7 +38,6 @@ cryptography = "*"
[tool.poetry.extras]
asyncmy = ["asyncmy"]
asyncpg = ["asyncpg"]
aiomysql = ["aiomysql"]
[build-system]
requires = ["poetry>=0.12"]

View File

@@ -65,6 +65,7 @@ class Product(Model):
class Meta:
unique_together = (("name", "type"),)
indexes = (("name", "type"),)
class Config(Model):

View File

@@ -17,6 +17,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -151,6 +152,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -242,6 +244,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -334,6 +337,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -512,6 +516,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -681,6 +686,7 @@ old_models_describe = {
"description": None,
"docstring": None,
"unique_together": [],
"indexes": [],
"pk_field": {
"name": "id",
"field_type": "IntField",
@@ -793,6 +799,7 @@ def test_migrate(mocker: MockerFixture):
"ALTER TABLE `configs` RENAME TO `config`",
"ALTER TABLE `product` RENAME COLUMN `image` TO `pic`",
"ALTER TABLE `email` RENAME COLUMN `id` TO `email_id`",
"ALTER TABLE `product` ADD INDEX `idx_product_name_869427` (`name`, `type_db_alias`)",
"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",
@@ -816,6 +823,7 @@ def test_migrate(mocker: MockerFixture):
"ALTER TABLE `config` RENAME TO `configs`",
"ALTER TABLE `product` RENAME COLUMN `pic` TO `image`",
"ALTER TABLE `email` RENAME COLUMN `email_id` TO `id`",
"ALTER TABLE `product` DROP INDEX `idx_product_name_869427`",
"ALTER TABLE `email` DROP INDEX `idx_email_email_4a1a33`",
"ALTER TABLE `product` DROP INDEX `uid_product_name_869427`",
"ALTER TABLE `product` ALTER COLUMN `view_num` DROP DEFAULT",
@@ -843,6 +851,7 @@ def test_migrate(mocker: MockerFixture):
'ALTER TABLE "product" RENAME COLUMN "image" TO "pic"',
'ALTER TABLE "user" ALTER COLUMN "password" TYPE VARCHAR(100) USING "password"::VARCHAR(100)',
'ALTER TABLE "user" DROP COLUMN "avatar"',
'CREATE INDEX "idx_product_name_869427" ON "product" ("name", "type_db_alias")',
'CREATE INDEX "idx_email_email_4a1a33" ON "email" ("email")',
'CREATE TABLE "email_user" ("email_id" INT NOT NULL REFERENCES "email" ("email_id") ON DELETE CASCADE,"user_id" INT NOT NULL REFERENCES "user" ("id") ON DELETE CASCADE)',
'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\';',
@@ -865,6 +874,7 @@ def test_migrate(mocker: MockerFixture):
'ALTER TABLE "product" RENAME COLUMN "pic" TO "image"',
'ALTER TABLE "user" ADD "avatar" VARCHAR(200) NOT NULL DEFAULT \'\'',
'ALTER TABLE "user" ALTER COLUMN "password" TYPE VARCHAR(200) USING "password"::VARCHAR(200)',
'DROP INDEX "idx_product_name_869427"',
'DROP INDEX "idx_email_email_4a1a33"',
'DROP INDEX "idx_user_usernam_9987ab"',
'DROP INDEX "uid_product_name_869427"',