Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
af63221875 | ||
|
359525716c | ||
|
7d3eb2e151 | ||
|
d8abf79449 | ||
|
aa9f40ae27 | ||
|
79b7ae343a | ||
|
6f5a9ab78c | ||
|
1e5a83c281 |
@@ -2,6 +2,15 @@
|
||||
|
||||
## 0.5
|
||||
|
||||
### 0.5.7
|
||||
|
||||
- Fix no module found error. (#188) (#189)
|
||||
|
||||
### 0.5.6
|
||||
|
||||
- Add `Command` class. (#148) (#141) (#123) (#106)
|
||||
- Fix: migrate doesn't use source_field in unique_together. (#181)
|
||||
|
||||
### 0.5.5
|
||||
|
||||
- Fix KeyError: 'src_folder' after upgrading aerich to 0.5.4. (#176)
|
||||
|
12
README.md
12
README.md
@@ -223,6 +223,18 @@ can make the following steps:
|
||||
|
||||
Note that these actions is safe, also you can do that to reset your migrations if your migration files is too many.
|
||||
|
||||
## Use `aerich` in application
|
||||
|
||||
You can use `aerich` out of cli by use `Command` class.
|
||||
|
||||
```python
|
||||
from aerich import Command
|
||||
|
||||
command = Command(tortoise_config=config, app='models')
|
||||
await command.init()
|
||||
await command.migrate('test')
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the
|
||||
|
@@ -1 +1,138 @@
|
||||
__version__ = "0.5.5"
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from tortoise import Tortoise, generate_schema_for_client
|
||||
from tortoise.exceptions import OperationalError
|
||||
from tortoise.transactions import in_transaction
|
||||
from tortoise.utils import get_schema_sql
|
||||
|
||||
from aerich.exceptions import DowngradeError
|
||||
from aerich.inspectdb import InspectDb
|
||||
from aerich.migrate import Migrate
|
||||
from aerich.models import Aerich
|
||||
from aerich.utils import (
|
||||
get_app_connection,
|
||||
get_app_connection_name,
|
||||
get_models_describe,
|
||||
get_version_content_from_file,
|
||||
write_version_file,
|
||||
)
|
||||
|
||||
|
||||
class Command:
|
||||
def __init__(
|
||||
self,
|
||||
tortoise_config: dict,
|
||||
app: str = "models",
|
||||
location: str = "./migrations",
|
||||
):
|
||||
self.tortoise_config = tortoise_config
|
||||
self.app = app
|
||||
self.location = location
|
||||
Migrate.app = app
|
||||
|
||||
async def init(self):
|
||||
await Migrate.init(self.tortoise_config, self.app, self.location)
|
||||
|
||||
async def upgrade(self):
|
||||
migrated = []
|
||||
for version_file in Migrate.get_all_version_files():
|
||||
try:
|
||||
exists = await Aerich.exists(version=version_file, app=self.app)
|
||||
except OperationalError:
|
||||
exists = False
|
||||
if not exists:
|
||||
async with in_transaction(
|
||||
get_app_connection_name(self.tortoise_config, self.app)
|
||||
) as conn:
|
||||
file_path = Path(Migrate.migrate_location, version_file)
|
||||
content = get_version_content_from_file(file_path)
|
||||
upgrade_query_list = content.get("upgrade")
|
||||
for upgrade_query in upgrade_query_list:
|
||||
await conn.execute_script(upgrade_query)
|
||||
await Aerich.create(
|
||||
version=version_file,
|
||||
app=self.app,
|
||||
content=get_models_describe(self.app),
|
||||
)
|
||||
migrated.append(version_file)
|
||||
return migrated
|
||||
|
||||
async def downgrade(self, version: int, delete: bool):
|
||||
ret = []
|
||||
if version == -1:
|
||||
specified_version = await Migrate.get_last_version()
|
||||
else:
|
||||
specified_version = await Aerich.filter(
|
||||
app=self.app, version__startswith=f"{version}_"
|
||||
).first()
|
||||
if not specified_version:
|
||||
raise DowngradeError("No specified version found")
|
||||
if version == -1:
|
||||
versions = [specified_version]
|
||||
else:
|
||||
versions = await Aerich.filter(app=self.app, pk__gte=specified_version.pk)
|
||||
for version in versions:
|
||||
file = version.version
|
||||
async with in_transaction(
|
||||
get_app_connection_name(self.tortoise_config, self.app)
|
||||
) as conn:
|
||||
file_path = Path(Migrate.migrate_location, file)
|
||||
content = get_version_content_from_file(file_path)
|
||||
downgrade_query_list = content.get("downgrade")
|
||||
if not downgrade_query_list:
|
||||
raise DowngradeError("No downgrade items found")
|
||||
for downgrade_query in downgrade_query_list:
|
||||
await conn.execute_query(downgrade_query)
|
||||
await version.delete()
|
||||
if delete:
|
||||
os.unlink(file_path)
|
||||
ret.append(file)
|
||||
return ret
|
||||
|
||||
async def heads(self):
|
||||
ret = []
|
||||
versions = Migrate.get_all_version_files()
|
||||
for version in versions:
|
||||
if not await Aerich.exists(version=version, app=self.app):
|
||||
ret.append(version)
|
||||
return ret
|
||||
|
||||
async def history(self):
|
||||
ret = []
|
||||
versions = Migrate.get_all_version_files()
|
||||
for version in versions:
|
||||
ret.append(version)
|
||||
return ret
|
||||
|
||||
async def inspectdb(self, tables: List[str]):
|
||||
connection = get_app_connection(self.tortoise_config, self.app)
|
||||
inspect = InspectDb(connection, tables)
|
||||
await inspect.inspect()
|
||||
|
||||
async def migrate(self, name: str = "update"):
|
||||
return await Migrate.migrate(name)
|
||||
|
||||
async def init_db(self, safe: bool):
|
||||
location = self.location
|
||||
app = self.app
|
||||
dirname = Path(location, app)
|
||||
dirname.mkdir(parents=True)
|
||||
|
||||
await Tortoise.init(config=self.tortoise_config)
|
||||
connection = get_app_connection(self.tortoise_config, app)
|
||||
await generate_schema_for_client(connection, safe)
|
||||
|
||||
schema = get_schema_sql(connection, safe)
|
||||
|
||||
version = await Migrate.generate_version()
|
||||
await Aerich.create(
|
||||
version=version,
|
||||
app=app,
|
||||
content=get_models_describe(app),
|
||||
)
|
||||
content = {
|
||||
"upgrade": [schema],
|
||||
}
|
||||
write_version_file(Path(dirname, version), content)
|
||||
|
169
aerich/cli.py
169
aerich/cli.py
@@ -7,26 +7,14 @@ from typing import List
|
||||
|
||||
import click
|
||||
from click import Context, UsageError
|
||||
from tortoise import Tortoise, generate_schema_for_client
|
||||
from tortoise.exceptions import OperationalError
|
||||
from tortoise.transactions import in_transaction
|
||||
from tortoise.utils import get_schema_sql
|
||||
from tortoise import Tortoise
|
||||
|
||||
from aerich.inspectdb import InspectDb
|
||||
from aerich.migrate import Migrate
|
||||
from aerich.utils import (
|
||||
add_src_path,
|
||||
get_app_connection,
|
||||
get_app_connection_name,
|
||||
get_models_describe,
|
||||
get_tortoise_config,
|
||||
get_version_content_from_file,
|
||||
write_version_file,
|
||||
)
|
||||
from aerich.exceptions import DowngradeError
|
||||
from aerich.utils import add_src_path, get_tortoise_config
|
||||
|
||||
from . import __version__
|
||||
from . import Command
|
||||
from .enums import Color
|
||||
from .models import Aerich
|
||||
from .version import __version__
|
||||
|
||||
parser = ConfigParser()
|
||||
|
||||
@@ -53,7 +41,11 @@ def coro(f):
|
||||
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
||||
@click.version_option(__version__, "-V", "--version")
|
||||
@click.option(
|
||||
"-c", "--config", default="aerich.ini", show_default=True, help="Config file.",
|
||||
"-c",
|
||||
"--config",
|
||||
default="aerich.ini",
|
||||
show_default=True,
|
||||
help="Config file.",
|
||||
)
|
||||
@click.option("--app", required=False, help="Tortoise-ORM app name.")
|
||||
@click.option(
|
||||
@@ -79,20 +71,15 @@ async def cli(ctx: Context, config, app, name):
|
||||
location = parser[name]["location"]
|
||||
tortoise_orm = parser[name]["tortoise_orm"]
|
||||
src_folder = parser[name].get("src_folder", CONFIG_DEFAULT_VALUES["src_folder"])
|
||||
|
||||
# Add specified source folder to path
|
||||
add_src_path(src_folder)
|
||||
|
||||
tortoise_config = get_tortoise_config(ctx, tortoise_orm)
|
||||
app = app or list(tortoise_config.get("apps").keys())[0]
|
||||
ctx.obj["config"] = tortoise_config
|
||||
ctx.obj["location"] = location
|
||||
ctx.obj["app"] = app
|
||||
Migrate.app = app
|
||||
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():
|
||||
raise UsageError("You must exec init-db first", ctx=ctx)
|
||||
await Migrate.init(tortoise_config, app, location)
|
||||
await command.init()
|
||||
|
||||
|
||||
@cli.command(help="Generate migrate changes file.")
|
||||
@@ -100,7 +87,8 @@ async def cli(ctx: Context, config, app, name):
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def migrate(ctx: Context, name):
|
||||
ret = await Migrate.migrate(name)
|
||||
command = ctx.obj["command"]
|
||||
ret = await command.migrate(name)
|
||||
if not ret:
|
||||
return click.secho("No changes detected", fg=Color.yellow)
|
||||
click.secho(f"Success migrate {ret}", fg=Color.green)
|
||||
@@ -110,28 +98,13 @@ async def migrate(ctx: Context, name):
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def upgrade(ctx: Context):
|
||||
config = ctx.obj["config"]
|
||||
app = ctx.obj["app"]
|
||||
migrated = False
|
||||
for version_file in Migrate.get_all_version_files():
|
||||
try:
|
||||
exists = await Aerich.exists(version=version_file, app=app)
|
||||
except OperationalError:
|
||||
exists = False
|
||||
if not exists:
|
||||
async with in_transaction(get_app_connection_name(config, app)) as conn:
|
||||
file_path = Path(Migrate.migrate_location, version_file)
|
||||
content = get_version_content_from_file(file_path)
|
||||
upgrade_query_list = content.get("upgrade")
|
||||
for upgrade_query in upgrade_query_list:
|
||||
await conn.execute_script(upgrade_query)
|
||||
await Aerich.create(
|
||||
version=version_file, app=app, content=get_models_describe(app),
|
||||
)
|
||||
click.secho(f"Success upgrade {version_file}", fg=Color.green)
|
||||
migrated = True
|
||||
command = ctx.obj["command"]
|
||||
migrated = await command.upgrade()
|
||||
if not migrated:
|
||||
click.secho("No upgrade items found", fg=Color.yellow)
|
||||
else:
|
||||
for version_file in migrated:
|
||||
click.secho(f"Success upgrade {version_file}", fg=Color.green)
|
||||
|
||||
|
||||
@cli.command(help="Downgrade to specified version.")
|
||||
@@ -157,59 +130,37 @@ async def upgrade(ctx: Context):
|
||||
)
|
||||
@coro
|
||||
async def downgrade(ctx: Context, version: int, delete: bool):
|
||||
app = ctx.obj["app"]
|
||||
config = ctx.obj["config"]
|
||||
if version == -1:
|
||||
specified_version = await Migrate.get_last_version()
|
||||
else:
|
||||
specified_version = await Aerich.filter(app=app, version__startswith=f"{version}_").first()
|
||||
if not specified_version:
|
||||
return click.secho("No specified version found", fg=Color.yellow)
|
||||
if version == -1:
|
||||
versions = [specified_version]
|
||||
else:
|
||||
versions = await Aerich.filter(app=app, pk__gte=specified_version.pk)
|
||||
for version in versions:
|
||||
file = version.version
|
||||
async with in_transaction(get_app_connection_name(config, app)) as conn:
|
||||
file_path = Path(Migrate.migrate_location, file)
|
||||
content = get_version_content_from_file(file_path)
|
||||
downgrade_query_list = content.get("downgrade")
|
||||
if not downgrade_query_list:
|
||||
click.secho("No downgrade items found", fg=Color.yellow)
|
||||
return
|
||||
for downgrade_query in downgrade_query_list:
|
||||
await conn.execute_query(downgrade_query)
|
||||
await version.delete()
|
||||
if delete:
|
||||
os.unlink(file_path)
|
||||
click.secho(f"Success downgrade {file}", fg=Color.green)
|
||||
command = ctx.obj["command"]
|
||||
try:
|
||||
files = await command.downgrade(version, delete)
|
||||
except DowngradeError as e:
|
||||
return click.secho(str(e), fg=Color.yellow)
|
||||
for file in files:
|
||||
click.secho(f"Success downgrade {file}", fg=Color.green)
|
||||
|
||||
|
||||
@cli.command(help="Show current available heads in migrate location.")
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def heads(ctx: Context):
|
||||
app = ctx.obj["app"]
|
||||
versions = Migrate.get_all_version_files()
|
||||
is_heads = False
|
||||
for version in versions:
|
||||
if not await Aerich.exists(version=version, app=app):
|
||||
click.secho(version, fg=Color.green)
|
||||
is_heads = True
|
||||
if not is_heads:
|
||||
click.secho("No available heads,try migrate first", fg=Color.green)
|
||||
command = ctx.obj["command"]
|
||||
head_list = await command.heads()
|
||||
if not head_list:
|
||||
return click.secho("No available heads, try migrate first", fg=Color.green)
|
||||
for version in head_list:
|
||||
click.secho(version, fg=Color.green)
|
||||
|
||||
|
||||
@cli.command(help="List all migrate items.")
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def history(ctx: Context):
|
||||
versions = Migrate.get_all_version_files()
|
||||
command = ctx.obj["command"]
|
||||
versions = await command.history()
|
||||
if not versions:
|
||||
return click.secho("No history, try migrate", fg=Color.green)
|
||||
for version in versions:
|
||||
click.secho(version, fg=Color.green)
|
||||
if not versions:
|
||||
click.secho("No history,try migrate", fg=Color.green)
|
||||
|
||||
|
||||
@cli.command(help="Init config file and generate root migrate location.")
|
||||
@@ -220,7 +171,10 @@ async def history(ctx: Context):
|
||||
help="Tortoise-ORM config module dict variable, like settings.TORTOISE_ORM.",
|
||||
)
|
||||
@click.option(
|
||||
"--location", default="./migrations", show_default=True, help="Migrate store location.",
|
||||
"--location",
|
||||
default="./migrations",
|
||||
show_default=True,
|
||||
help="Migrate store location.",
|
||||
)
|
||||
@click.option(
|
||||
"-s",
|
||||
@@ -272,49 +226,32 @@ async def init(ctx: Context, tortoise_orm, location, src_folder):
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def init_db(ctx: Context, safe):
|
||||
config = ctx.obj["config"]
|
||||
location = ctx.obj["location"]
|
||||
app = ctx.obj["app"]
|
||||
|
||||
dirname = Path(location, app)
|
||||
command = ctx.obj["command"]
|
||||
app = command.app
|
||||
dirname = Path(command.location, app)
|
||||
try:
|
||||
dirname.mkdir(parents=True)
|
||||
await command.init_db(safe)
|
||||
click.secho(f"Success create app migrate location {dirname}", fg=Color.green)
|
||||
click.secho(f'Success generate schema for app "{app}"', fg=Color.green)
|
||||
except FileExistsError:
|
||||
return click.secho(
|
||||
f"Inited {app} already, or delete {dirname} and try again.", fg=Color.yellow
|
||||
)
|
||||
|
||||
await Tortoise.init(config=config)
|
||||
connection = get_app_connection(config, app)
|
||||
await generate_schema_for_client(connection, safe)
|
||||
|
||||
schema = get_schema_sql(connection, safe)
|
||||
|
||||
version = await Migrate.generate_version()
|
||||
await Aerich.create(
|
||||
version=version, app=app, content=get_models_describe(app),
|
||||
)
|
||||
content = {
|
||||
"upgrade": [schema],
|
||||
}
|
||||
write_version_file(Path(dirname, version), content)
|
||||
click.secho(f'Success generate schema for app "{app}"', fg=Color.green)
|
||||
|
||||
|
||||
@cli.command(help="Introspects the database tables to standard output as TortoiseORM model.")
|
||||
@click.option(
|
||||
"-t", "--table", help="Which tables to inspect.", multiple=True, required=False,
|
||||
"-t",
|
||||
"--table",
|
||||
help="Which tables to inspect.",
|
||||
multiple=True,
|
||||
required=False,
|
||||
)
|
||||
@click.pass_context
|
||||
@coro
|
||||
async def inspectdb(ctx: Context, table: List[str]):
|
||||
config = ctx.obj["config"]
|
||||
app = ctx.obj["app"]
|
||||
connection = get_app_connection(config, app)
|
||||
|
||||
inspect = InspectDb(connection, table)
|
||||
await inspect.inspect()
|
||||
command = ctx.obj["command"]
|
||||
await command.inspectdb(table)
|
||||
|
||||
|
||||
def main():
|
||||
|
@@ -78,11 +78,15 @@ class BaseDDL:
|
||||
auto_now_add = field_describe.get("auto_now_add", False)
|
||||
auto_now = field_describe.get("auto_now", False)
|
||||
if default is not None or auto_now_add:
|
||||
if field_describe.get("field_type") in [
|
||||
"UUIDField",
|
||||
"TextField",
|
||||
"JSONField",
|
||||
] or is_default_function(default):
|
||||
if (
|
||||
field_describe.get("field_type")
|
||||
in [
|
||||
"UUIDField",
|
||||
"TextField",
|
||||
"JSONField",
|
||||
]
|
||||
or is_default_function(default)
|
||||
):
|
||||
default = ""
|
||||
else:
|
||||
try:
|
||||
@@ -115,7 +119,9 @@ class BaseDDL:
|
||||
nullable="NOT NULL" if not field_describe.get("nullable") else "",
|
||||
unique="UNIQUE" if field_describe.get("unique") else "",
|
||||
comment=self.schema_generator._column_comment_generator(
|
||||
table=db_table, column=db_column, comment=field_describe.get("description"),
|
||||
table=db_table,
|
||||
column=db_column,
|
||||
comment=field_describe.get("description"),
|
||||
)
|
||||
if description
|
||||
else "",
|
||||
|
@@ -2,3 +2,9 @@ class NotSupportError(Exception):
|
||||
"""
|
||||
raise when features not support
|
||||
"""
|
||||
|
||||
|
||||
class DowngradeError(Exception):
|
||||
"""
|
||||
raise when downgrade error
|
||||
"""
|
||||
|
@@ -264,7 +264,11 @@ class Migrate:
|
||||
# rename field
|
||||
if (
|
||||
changes[0]
|
||||
== ("change", "name", (old_data_field_name, new_data_field_name),)
|
||||
== (
|
||||
"change",
|
||||
"name",
|
||||
(old_data_field_name, new_data_field_name),
|
||||
)
|
||||
and changes[1]
|
||||
== (
|
||||
"change",
|
||||
@@ -302,11 +306,16 @@ class Migrate:
|
||||
)
|
||||
else:
|
||||
cls._add_operator(
|
||||
cls._rename_field(model, *changes[1][2]), upgrade,
|
||||
cls._rename_field(model, *changes[1][2]),
|
||||
upgrade,
|
||||
)
|
||||
if not is_rename:
|
||||
cls._add_operator(
|
||||
cls._add_field(model, new_data_field,), upgrade,
|
||||
cls._add_field(
|
||||
model,
|
||||
new_data_field,
|
||||
),
|
||||
upgrade,
|
||||
)
|
||||
# remove fields
|
||||
for old_data_field_name in set(old_data_fields_name).difference(
|
||||
@@ -406,7 +415,8 @@ class Migrate:
|
||||
else:
|
||||
# modify column
|
||||
cls._add_operator(
|
||||
cls._modify_field(model, new_data_field), upgrade,
|
||||
cls._modify_field(model, new_data_field),
|
||||
upgrade,
|
||||
)
|
||||
|
||||
for old_model in old_models:
|
||||
@@ -437,7 +447,10 @@ class Migrate:
|
||||
def _resolve_fk_fields_name(cls, model: Type[Model], fields_name: Tuple[str]):
|
||||
ret = []
|
||||
for field_name in fields_name:
|
||||
if field_name in model._meta.fk_fields:
|
||||
field = model._meta.fields_map[field_name]
|
||||
if field.source_field:
|
||||
ret.append(field.source_field)
|
||||
elif field_name in model._meta.fk_fields:
|
||||
ret.append(field_name + "_id")
|
||||
else:
|
||||
ret.append(field_name)
|
||||
|
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
from typing import Dict, Union
|
||||
|
||||
from click import BadOptionUsage, ClickException, Context
|
||||
from tortoise import BaseDBAsyncClient, Tortoise
|
||||
@@ -36,7 +36,8 @@ def get_app_connection_name(config, app_name: str) -> str:
|
||||
if app:
|
||||
return app.get("default_connection", "default")
|
||||
raise BadOptionUsage(
|
||||
option_name="--app", message=f'Can\'t get app named "{app_name}"',
|
||||
option_name="--app",
|
||||
message=f'Can\'t get app named "{app_name}"',
|
||||
)
|
||||
|
||||
|
||||
@@ -80,7 +81,7 @@ _UPGRADE = "-- upgrade --\n"
|
||||
_DOWNGRADE = "-- downgrade --\n"
|
||||
|
||||
|
||||
def get_version_content_from_file(version_file: str) -> Dict:
|
||||
def get_version_content_from_file(version_file: Union[str, Path]) -> Dict:
|
||||
"""
|
||||
get version content
|
||||
:param version_file:
|
||||
|
1
aerich/version.py
Normal file
1
aerich/version.py
Normal file
@@ -0,0 +1 @@
|
||||
__version__ = "0.5.7"
|
100
poetry.lock
generated
100
poetry.lock
generated
@@ -86,23 +86,27 @@ stevedore = ">=1.20.0"
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "19.10b0"
|
||||
version = "21.7b0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = "*"
|
||||
attrs = ">=18.1.0"
|
||||
click = ">=6.5"
|
||||
pathspec = ">=0.6,<1"
|
||||
regex = "*"
|
||||
toml = ">=0.9.4"
|
||||
typed-ast = ">=1.4.0"
|
||||
click = ">=7.1.2"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.8.1,<1"
|
||||
regex = ">=2020.1.8"
|
||||
tomli = ">=0.2.6,<2.0.0"
|
||||
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""}
|
||||
typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"]
|
||||
python2 = ["typed-ast (>=1.4.2)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
@@ -167,17 +171,17 @@ pyparsing = "*"
|
||||
|
||||
[[package]]
|
||||
name = "dictdiffer"
|
||||
version = "0.8.1"
|
||||
version = "0.9.0"
|
||||
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)"]
|
||||
all = ["Sphinx (>=3)", "sphinx-rtd-theme (>=0.2)", "check-manifest (>=0.42)", "mock (>=1.3.0)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "sphinx (>=3)", "tox (>=3.7.0)", "numpy (>=1.13.0)", "numpy (>=1.15.0)", "numpy (>=1.18.0)", "pytest (==5.4.3)", "pytest-pycodestyle (>=2)", "pytest-pydocstyle (>=2)", "pytest (>=6)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)", "numpy (>=1.20.0)"]
|
||||
docs = ["Sphinx (>=3)", "sphinx-rtd-theme (>=0.2)"]
|
||||
numpy = ["numpy (>=1.13.0)", "numpy (>=1.15.0)", "numpy (>=1.18.0)", "numpy (>=1.20.0)"]
|
||||
tests = ["check-manifest (>=0.42)", "mock (>=1.3.0)", "pytest-cov (>=2.10.1)", "pytest-isort (>=1.2.0)", "sphinx (>=3)", "tox (>=3.7.0)", "pytest (==5.4.3)", "pytest-pycodestyle (>=2)", "pytest-pydocstyle (>=2)", "pytest (>=6)", "pytest-pycodestyle (>=2.2.0)", "pytest-pydocstyle (>=2.2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "execnet"
|
||||
@@ -217,7 +221,7 @@ smmap = ">=3.0.1,<5"
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.18"
|
||||
version = "3.1.19"
|
||||
description = "Python Git Library"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -225,7 +229,7 @@ python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
gitdb = ">=4.0.1,<5"
|
||||
typing-extensions = {version = ">=3.7.4.0", markers = "python_version < \"3.8\""}
|
||||
typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""}
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
@@ -282,6 +286,14 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.0"
|
||||
@@ -536,32 +548,33 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "1.1.0"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "tortoise-orm"
|
||||
version = "0.17.5"
|
||||
version = "0.17.6"
|
||||
description = "Easy async ORM for python, built with relations in mind"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "^3.7"
|
||||
develop = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
|
||||
[package.dependencies]
|
||||
aiosqlite = "^0.16.0"
|
||||
iso8601 = "^0.1.13"
|
||||
pypika-tortoise = "^0.1.1"
|
||||
aiosqlite = ">=0.16.0,<0.17.0"
|
||||
iso8601 = ">=0.1.13,<0.2.0"
|
||||
pypika-tortoise = ">=0.1.1,<0.2.0"
|
||||
pytz = "*"
|
||||
|
||||
[package.extras]
|
||||
accel = ["ciso8601 (>=2.1.2,<3.0.0)", "uvloop (>=0.14.0,<0.15.0)", "python-rapidjson"]
|
||||
asyncpg = ["asyncpg"]
|
||||
aiomysql = ["aiomysql"]
|
||||
asyncmy = ["asyncmy"]
|
||||
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/tortoise/tortoise-orm.git"
|
||||
reference = "develop"
|
||||
resolved_reference = "879a8f67080e3f0a9787ba2ba698849c976a7d2b"
|
||||
asyncpg = ["asyncpg"]
|
||||
accel = ["ciso8601 (>=2.1.2,<3.0.0)", "python-rapidjson", "uvloop (>=0.14.0,<0.15.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "typed-ast"
|
||||
@@ -599,7 +612,7 @@ asyncpg = ["asyncpg"]
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "c1fbe55dd8ab1a3e138ff8afc79bbbf930c4a70df42299bb657b39c5db45d692"
|
||||
content-hash = "27ad84abe8f9971328cd6ddc239d03dd1212eef230174e7447596a534f6e8be8"
|
||||
|
||||
[metadata.files]
|
||||
aiomysql = [
|
||||
@@ -644,8 +657,8 @@ bandit = [
|
||||
{file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
|
||||
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
|
||||
{file = "black-21.7b0-py3-none-any.whl", hash = "sha256:1c7aa6ada8ee864db745b22790a32f94b2795c253a75d6d9b5e439ff10d23116"},
|
||||
{file = "black-21.7b0.tar.gz", hash = "sha256:c8373c6491de9362e39271630b65b964607bc5c79c83783547d76c839b3aa219"},
|
||||
]
|
||||
cffi = [
|
||||
{file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
|
||||
@@ -716,8 +729,8 @@ ddlparse = [
|
||||
{file = "ddlparse-1.10.0.tar.gz", hash = "sha256:6418681baa848eb01251ab79eb3d0ad7e140e6ab1deaae5a019353ddb3a908da"},
|
||||
]
|
||||
dictdiffer = [
|
||||
{file = "dictdiffer-0.8.1-py2.py3-none-any.whl", hash = "sha256:d79d9a39e459fe33497c858470ca0d2e93cb96621751de06d631856adfd9c390"},
|
||||
{file = "dictdiffer-0.8.1.tar.gz", hash = "sha256:1adec0d67cdf6166bda96ae2934ddb5e54433998ceab63c984574d187cc563d2"},
|
||||
{file = "dictdiffer-0.9.0-py2.py3-none-any.whl", hash = "sha256:442bfc693cfcadaf46674575d2eba1c53b42f5e404218ca2c2ff549f2df56595"},
|
||||
{file = "dictdiffer-0.9.0.tar.gz", hash = "sha256:17bacf5fbfe613ccf1b6d512bd766e6b21fb798822a133aa86098b8ac9997578"},
|
||||
]
|
||||
execnet = [
|
||||
{file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
|
||||
@@ -732,8 +745,8 @@ gitdb = [
|
||||
{file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"},
|
||||
]
|
||||
gitpython = [
|
||||
{file = "GitPython-3.1.18-py3-none-any.whl", hash = "sha256:fce760879cd2aebd2991b3542876dc5c4a909b30c9d69dfc488e504a8db37ee8"},
|
||||
{file = "GitPython-3.1.18.tar.gz", hash = "sha256:b838a895977b45ab6f0cc926a9045c8d1c44e2b653c1fcc39fe91f42c6e8f05b"},
|
||||
{file = "GitPython-3.1.19-py3-none-any.whl", hash = "sha256:17690588e36bd0cee21921ce621fad1e8be45a37afa486ff846fb8efcf53c34c"},
|
||||
{file = "GitPython-3.1.19.tar.gz", hash = "sha256:18f4039b96b5567bc4745eb851737ce456a2d499cecd71e84f5c0950e92d0e53"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.6.1-py3-none-any.whl", hash = "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"},
|
||||
@@ -755,6 +768,10 @@ mccabe = [
|
||||
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
|
||||
{file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
|
||||
@@ -929,7 +946,14 @@ toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
tortoise-orm = []
|
||||
tomli = [
|
||||
{file = "tomli-1.1.0-py3-none-any.whl", hash = "sha256:f4a182048010e89cbec0ae4686b21f550a7f2903f665e34a6de58ec15424f919"},
|
||||
{file = "tomli-1.1.0.tar.gz", hash = "sha256:33d7984738f8bb699c9b0a816eb646a8178a69eaa792d258486776a5d21b8ca5"},
|
||||
]
|
||||
tortoise-orm = [
|
||||
{file = "tortoise-orm-0.17.6.tar.gz", hash = "sha256:f4187cd94b838cdf16aaa4e1ec54852d773c1a37a2a510d05f648d7350ab3c6e"},
|
||||
{file = "tortoise_orm-0.17.6-py3-none-any.whl", hash = "sha256:294b94d04d18cf85cca2a93f708be720005625b5dae3af8e8190f050ddb718d2"},
|
||||
]
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"},
|
||||
{file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "aerich"
|
||||
version = "0.5.5"
|
||||
version = "0.5.7"
|
||||
description = "A database migrations tool for Tortoise ORM."
|
||||
authors = ["long2ice <long2ice@gmail.com>"]
|
||||
license = "Apache-2.0"
|
||||
@@ -16,7 +16,7 @@ include = ["CHANGELOG.md", "LICENSE", "README.md"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
tortoise-orm = { git = "https://github.com/tortoise/tortoise-orm.git", branch = "develop" }
|
||||
tortoise-orm = "*"
|
||||
click = "*"
|
||||
pydantic = "*"
|
||||
aiomysql = { version = "*", optional = true }
|
||||
@@ -27,7 +27,7 @@ dictdiffer = "*"
|
||||
[tool.poetry.dev-dependencies]
|
||||
flake8 = "*"
|
||||
isort = "*"
|
||||
black = "19.10b0"
|
||||
black = "*"
|
||||
pytest = "*"
|
||||
pytest-xdist = "*"
|
||||
pytest-asyncio = "*"
|
||||
|
@@ -56,7 +56,9 @@ class Product(Model):
|
||||
view_num = fields.IntField(description="View Num", default=0)
|
||||
sort = fields.IntField()
|
||||
is_reviewed = fields.BooleanField(description="Is Reviewed")
|
||||
type = fields.IntEnumField(ProductType, description="Product Type")
|
||||
type = fields.IntEnumField(
|
||||
ProductType, description="Product Type", source_field="type_db_alias"
|
||||
)
|
||||
pic = fields.CharField(max_length=200)
|
||||
body = fields.TextField()
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
|
@@ -50,7 +50,9 @@ class Product(Model):
|
||||
view_num = fields.IntField(description="View Num")
|
||||
sort = fields.IntField()
|
||||
is_reviewed = fields.BooleanField(description="Is Reviewed")
|
||||
type = fields.IntEnumField(ProductType, description="Product Type")
|
||||
type = fields.IntEnumField(
|
||||
ProductType, description="Product Type", source_field="type_db_alias"
|
||||
)
|
||||
image = fields.CharField(max_length=200)
|
||||
body = fields.TextField()
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
|
@@ -50,7 +50,9 @@ class Product(Model):
|
||||
view_num = fields.IntField(description="View Num")
|
||||
sort = fields.IntField()
|
||||
is_reviewed = fields.BooleanField(description="Is Reviewed")
|
||||
type = fields.IntEnumField(ProductType, description="Product Type")
|
||||
type = fields.IntEnumField(
|
||||
ProductType, description="Product Type", source_field="type_db_alias"
|
||||
)
|
||||
image = fields.CharField(max_length=200)
|
||||
body = fields.TextField()
|
||||
created_at = fields.DatetimeField(auto_now_add=True)
|
||||
|
@@ -413,7 +413,7 @@ old_models_describe = {
|
||||
{
|
||||
"name": "type",
|
||||
"field_type": "IntEnumFieldInstance",
|
||||
"db_column": "type",
|
||||
"db_column": "type_db_alias",
|
||||
"python_type": "int",
|
||||
"generated": False,
|
||||
"nullable": False,
|
||||
@@ -794,7 +794,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `product` RENAME COLUMN `image` TO `pic`",
|
||||
"ALTER TABLE `email` RENAME COLUMN `id` TO `email_id`",
|
||||
"ALTER TABLE `email` ADD INDEX `idx_email_email_4a1a33` (`email`)",
|
||||
"ALTER TABLE `product` ADD UNIQUE INDEX `uid_product_name_f14935` (`name`, `type`)",
|
||||
"ALTER TABLE `product` ADD UNIQUE INDEX `uid_product_name_869427` (`name`, `type_db_alias`)",
|
||||
"ALTER TABLE `product` ALTER COLUMN `view_num` SET DEFAULT 0",
|
||||
"ALTER TABLE `user` DROP COLUMN `avatar`",
|
||||
"ALTER TABLE `user` MODIFY COLUMN `password` VARCHAR(100) NOT NULL",
|
||||
@@ -817,7 +817,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
"ALTER TABLE `product` RENAME COLUMN `pic` TO `image`",
|
||||
"ALTER TABLE `email` RENAME COLUMN `email_id` TO `id`",
|
||||
"ALTER TABLE `email` DROP INDEX `idx_email_email_4a1a33`",
|
||||
"ALTER TABLE `product` DROP INDEX `uid_product_name_f14935`",
|
||||
"ALTER TABLE `product` DROP INDEX `uid_product_name_869427`",
|
||||
"ALTER TABLE `product` ALTER COLUMN `view_num` DROP DEFAULT",
|
||||
"ALTER TABLE `user` ADD `avatar` VARCHAR(200) NOT NULL DEFAULT ''",
|
||||
"ALTER TABLE `user` DROP INDEX `idx_user_usernam_9987ab`",
|
||||
@@ -846,7 +846,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
'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\';',
|
||||
'CREATE UNIQUE INDEX "uid_product_name_f14935" ON "product" ("name", "type")',
|
||||
'CREATE UNIQUE INDEX "uid_product_name_869427" ON "product" ("name", "type_db_alias")',
|
||||
'CREATE UNIQUE INDEX "uid_user_usernam_9987ab" ON "user" ("username")',
|
||||
]
|
||||
)
|
||||
@@ -867,7 +867,7 @@ def test_migrate(mocker: MockerFixture):
|
||||
'ALTER TABLE "user" ALTER COLUMN "password" TYPE VARCHAR(200) USING "password"::VARCHAR(200)',
|
||||
'DROP INDEX "idx_email_email_4a1a33"',
|
||||
'DROP INDEX "idx_user_usernam_9987ab"',
|
||||
'DROP INDEX "uid_product_name_f14935"',
|
||||
'DROP INDEX "uid_product_name_869427"',
|
||||
'DROP TABLE IF EXISTS "email_user"',
|
||||
'DROP TABLE IF EXISTS "newmodel"',
|
||||
]
|
||||
|
Reference in New Issue
Block a user