add migrate and cli
This commit is contained in:
@@ -1,11 +1 @@
|
||||
__version__ = '0.1.0'
|
||||
|
||||
from alice.cmd import CommandLine
|
||||
|
||||
|
||||
def main(argv):
|
||||
command = CommandLine(argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(None)
|
||||
|
||||
@@ -63,7 +63,7 @@ class DDL:
|
||||
)
|
||||
if field_object.description else "",
|
||||
is_primary_key=field_object.pk,
|
||||
default=default,
|
||||
default=default
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
83
alice/cli.py
Normal file
83
alice/cli.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from enum import Enum
|
||||
|
||||
import click
|
||||
from click import BadParameter, ClickException
|
||||
|
||||
sys.path.append(os.getcwd())
|
||||
|
||||
|
||||
class Color(str, Enum):
|
||||
green = 'green'
|
||||
red = 'red'
|
||||
|
||||
|
||||
@click.group(context_settings={'help_option_names': ['-h', '--help']})
|
||||
@click.option('-c', '--config', default='settings',
|
||||
help='Tortoise-ORM config module, will read config variable from it, default is `settings`.')
|
||||
@click.option('-t', '--tortoise-orm', default='TORTOISE_ORM',
|
||||
help='Tortoise-ORM config dict variable, default is `TORTOISE_ORM`.')
|
||||
@click.option('-l', '--location', default='./migrations',
|
||||
help='Migrate store location, default is `./migrations`.')
|
||||
@click.option('--connection', default='default', help='Tortoise-ORM connection name, default is `default`.')
|
||||
@click.pass_context
|
||||
def cli(ctx, config, tortoise_orm, location, connection):
|
||||
ctx.ensure_object(dict)
|
||||
try:
|
||||
config_module = importlib.import_module(config)
|
||||
config = getattr(config_module, tortoise_orm, None)
|
||||
if not config:
|
||||
raise BadParameter(param_hint=['--config'],
|
||||
message=f'Can\'t get "{tortoise_orm}" from module "{config_module}"')
|
||||
ctx.obj['config'] = config
|
||||
ctx.obj['location'] = location
|
||||
if connection not in config.get('connections').keys():
|
||||
raise BadParameter(param_hint=['--connection'], message=f'No connection found in "{config}"')
|
||||
except ModuleNotFoundError:
|
||||
raise BadParameter(param_hint=['--tortoise-orm'], message=f'No module named "{config}"')
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def migrate(ctx):
|
||||
config = ctx.obj['config']
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def upgrade():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def downgrade():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def initdb():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--overwrite', type=bool, default=False, help='Overwrite old_models.py.')
|
||||
@click.pass_context
|
||||
def init(ctx, overwrite):
|
||||
location = ctx.obj['location']
|
||||
config = ctx.obj['config']
|
||||
if not os.path.isdir(location) or overwrite:
|
||||
os.mkdir(location)
|
||||
connections = config.get('connections').keys()
|
||||
for connection in connections:
|
||||
dirname = os.path.join(location, connection)
|
||||
if not os.path.isdir(dirname):
|
||||
os.mkdir(dirname)
|
||||
click.secho(f'Success create migrate location {dirname}', fg=Color.green)
|
||||
if overwrite:
|
||||
pass
|
||||
else:
|
||||
raise ClickException('Already inited')
|
||||
18
alice/cmd.py
18
alice/cmd.py
@@ -1,18 +0,0 @@
|
||||
class CommandLine:
|
||||
def __init__(self, argv):
|
||||
self.argv = argv
|
||||
|
||||
def migrate(self):
|
||||
pass
|
||||
|
||||
def upgrade(self):
|
||||
pass
|
||||
|
||||
def downgrade(self):
|
||||
pass
|
||||
|
||||
def init_db(self):
|
||||
pass
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
88
alice/migrate.py
Normal file
88
alice/migrate.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import importlib
|
||||
import inspect
|
||||
from typing import List, Type, Dict
|
||||
|
||||
from tortoise import Model, ForeignKeyFieldInstance
|
||||
from tortoise.fields import Field
|
||||
|
||||
from alice.backends import DDL
|
||||
|
||||
|
||||
class Migrate:
|
||||
operators: List
|
||||
ddl: DDL
|
||||
|
||||
def __init__(self, ddl: DDL):
|
||||
self.operators = []
|
||||
self.ddl = ddl
|
||||
|
||||
def diff_models_module(self, old_models_module, new_models_module):
|
||||
old_module = importlib.import_module(old_models_module)
|
||||
old_models = {}
|
||||
new_models = {}
|
||||
for name, obj in inspect.getmembers(old_module):
|
||||
if inspect.isclass(obj) and issubclass(obj, Model):
|
||||
old_models[obj.__name__] = obj
|
||||
|
||||
new_module = importlib.import_module(new_models_module)
|
||||
for name, obj in inspect.getmembers(new_module):
|
||||
if inspect.isclass(obj) and issubclass(obj, Model):
|
||||
new_models[obj.__name__] = obj
|
||||
self.diff_models(old_models, new_models)
|
||||
|
||||
def diff_models(self, old_models: Dict[str, Type[Model]], new_models: Dict[str, Type[Model]]):
|
||||
for new_model_str, new_model in new_models.items():
|
||||
if new_model_str not in old_models.keys():
|
||||
self.add_model(new_model)
|
||||
else:
|
||||
self.diff_model(old_models.get(new_model_str), new_model)
|
||||
|
||||
for old_model in old_models:
|
||||
if old_model not in new_models.keys():
|
||||
self.remove_model(old_models.get(old_model))
|
||||
|
||||
def _add_operator(self, operator):
|
||||
self.operators.append(operator)
|
||||
|
||||
def add_model(self, model: Type[Model]):
|
||||
self._add_operator(self.ddl.create_table(model))
|
||||
|
||||
def remove_model(self, model: Type[Model]):
|
||||
self._add_operator(self.ddl.drop_table(model))
|
||||
|
||||
def diff_model(self, old_model: Type[Model], new_model: Type[Model]):
|
||||
old_fields_map = old_model._meta.fields_map
|
||||
new_fields_map = new_model._meta.fields_map
|
||||
old_keys = old_fields_map.keys()
|
||||
new_keys = new_fields_map.keys()
|
||||
for new_key in new_keys:
|
||||
new_field = new_fields_map.get(new_key)
|
||||
if new_key not in old_keys:
|
||||
self._add_field(new_model, new_field)
|
||||
else:
|
||||
old_field = old_fields_map.get(new_key)
|
||||
if old_field.index and not new_field.index:
|
||||
self._remove_index(old_model, old_field)
|
||||
elif new_field.index and not old_field.index:
|
||||
self._add_index(new_model, new_field)
|
||||
for old_key in old_keys:
|
||||
if old_key not in new_keys:
|
||||
field = old_fields_map.get(old_key)
|
||||
self._remove_field(old_model, field)
|
||||
|
||||
def _remove_index(self, model: Type[Model], field: Field):
|
||||
self._add_operator(self.ddl.drop_index(model, [field.model_field_name], field.unique))
|
||||
|
||||
def _add_index(self, model: Type[Model], field: Field):
|
||||
self._add_operator(self.ddl.add_index(model, [field.model_field_name], field.unique))
|
||||
|
||||
def _add_field(self, model: Type[Model], field: Field):
|
||||
if isinstance(field, ForeignKeyFieldInstance):
|
||||
self._add_operator(self.ddl.add_fk(model, field))
|
||||
else:
|
||||
self._add_operator(self.ddl.add_column(model, field))
|
||||
|
||||
def _remove_field(self, model: Type[Model], field: Field):
|
||||
if isinstance(field, ForeignKeyFieldInstance):
|
||||
self._add_operator(self.ddl.drop_fk(model, field))
|
||||
self._add_operator(self.ddl.drop_column(model, field.model_field_name))
|
||||
17
alice/utils.py
Normal file
17
alice/utils.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import re
|
||||
|
||||
|
||||
def cp_models(old_model_file, new_model_file, new_app):
|
||||
"""
|
||||
cp models file to old_models.py and rename model app
|
||||
:param old_app:
|
||||
:param new_app:
|
||||
:param old_model_file:
|
||||
:param new_model_file:
|
||||
:return:r
|
||||
"""
|
||||
pattern = r'(ManyToManyField|ForeignKeyField|OneToOneField)\((model_name)?(\"|\')(?P<app>\w+).+\)'
|
||||
with open(old_model_file, 'r') as f:
|
||||
content = f.read()
|
||||
ret = re.sub(pattern, rf'{new_app} \g<app>', content)
|
||||
print(ret)
|
||||
Reference in New Issue
Block a user