add migrate and cli
This commit is contained in:
parent
4e7e1626aa
commit
a5a5de529b
3
MANIFEST.in
Normal file
3
MANIFEST.in
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
include LICENSE
|
||||||
|
include README.rst
|
||||||
|
include requirements.txt
|
@ -1,11 +1 @@
|
|||||||
__version__ = '0.1.0'
|
__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 "",
|
if field_object.description else "",
|
||||||
is_primary_key=field_object.pk,
|
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)
|
24
poetry.lock
generated
24
poetry.lock
generated
@ -48,6 +48,14 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Composable command line interface toolkit"
|
||||||
|
name = "click"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
|
version = "7.1.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||||
@ -173,7 +181,7 @@ description = "Easy async ORM for python, built with relations in mind"
|
|||||||
name = "tortoise-orm"
|
name = "tortoise-orm"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
aiosqlite = ">=0.11.0"
|
aiosqlite = ">=0.11.0"
|
||||||
@ -182,6 +190,10 @@ iso8601 = ">=0.1.12"
|
|||||||
pypika = ">=0.36.5"
|
pypika = ">=0.36.5"
|
||||||
typing-extensions = ">=3.7"
|
typing-extensions = ">=3.7"
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
reference = "72f84f0848dc68041157f03e60cd1c92b0ee5137"
|
||||||
|
type = "git"
|
||||||
|
url = "https://github.com/tortoise/tortoise-orm.git"
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||||
@ -191,7 +203,7 @@ python-versions = "*"
|
|||||||
version = "3.7.4.2"
|
version = "3.7.4.2"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "0b4c3c6eb6ed2e84b03745542ebc42dc53e6e5466bea0c4eee991bcd063a6ef3"
|
content-hash = "708876857d4653fd45cb251e9a1689c4158966f42da2efca1e8167becef89837"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@ -240,6 +252,10 @@ cffi = [
|
|||||||
ciso8601 = [
|
ciso8601 = [
|
||||||
{file = "ciso8601-2.1.3.tar.gz", hash = "sha256:bdbb5b366058b1c87735603b23060962c439ac9be66f1ae91e8c7dbd7d59e262"},
|
{file = "ciso8601-2.1.3.tar.gz", hash = "sha256:bdbb5b366058b1c87735603b23060962c439ac9be66f1ae91e8c7dbd7d59e262"},
|
||||||
]
|
]
|
||||||
|
click = [
|
||||||
|
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||||
|
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||||
|
]
|
||||||
cryptography = [
|
cryptography = [
|
||||||
{file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"},
|
{file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"},
|
||||||
{file = "cryptography-2.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b"},
|
{file = "cryptography-2.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b"},
|
||||||
@ -306,9 +322,7 @@ toml = [
|
|||||||
{file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"},
|
{file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"},
|
||||||
{file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"},
|
{file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"},
|
||||||
]
|
]
|
||||||
tortoise-orm = [
|
tortoise-orm = []
|
||||||
{file = "tortoise-orm-0.16.10.tar.gz", hash = "sha256:b3f4fdc9edabfc88413b7c5297b6cb9408420d1a97d9ad25051170b2b6228e02"},
|
|
||||||
]
|
|
||||||
typing-extensions = [
|
typing-extensions = [
|
||||||
{file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"},
|
{file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"},
|
||||||
{file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"},
|
{file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"},
|
||||||
|
@ -6,8 +6,9 @@ authors = ["long2ice <long2ice@gmail.com>"]
|
|||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8"
|
python = "^3.8"
|
||||||
tortoise-orm = "*"
|
tortoise-orm = {git = "https://github.com/tortoise/tortoise-orm.git", branch = "develop"}
|
||||||
aiomysql = "*"
|
aiomysql = "*"
|
||||||
|
click = "*"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
taskipy = "*"
|
taskipy = "*"
|
||||||
|
@ -2,6 +2,7 @@ aiomysql==0.0.20
|
|||||||
aiosqlite==0.13.0
|
aiosqlite==0.13.0
|
||||||
cffi==1.14.0
|
cffi==1.14.0
|
||||||
ciso8601==2.1.3; sys_platform != "win32" and implementation_name == "cpython"
|
ciso8601==2.1.3; sys_platform != "win32" and implementation_name == "cpython"
|
||||||
|
click==7.1.2
|
||||||
cryptography==2.9.2
|
cryptography==2.9.2
|
||||||
iso8601==0.1.12; sys_platform == "win32" or implementation_name != "cpython"
|
iso8601==0.1.12; sys_platform == "win32" or implementation_name != "cpython"
|
||||||
pycparser==2.20
|
pycparser==2.20
|
||||||
|
43
setup.py
43
setup.py
@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
|
||||||
|
def version():
|
||||||
|
ver_str_line = open('alice/__init__.py', 'rt').read()
|
||||||
|
mob = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", ver_str_line, re.M)
|
||||||
|
if not mob:
|
||||||
|
raise RuntimeError("Unable to find version string")
|
||||||
|
return mob.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as f:
|
||||||
|
long_description = f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def requirements():
|
||||||
|
return open('requirements.txt', 'rt').read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='alice',
|
||||||
|
version=version(),
|
||||||
|
description='A database migrations tool for Tortoise-ORM.',
|
||||||
|
author='long2ice',
|
||||||
|
long_description_content_type='text/x-rst',
|
||||||
|
long_description=long_description,
|
||||||
|
author_email='long2ice@gmail.com',
|
||||||
|
url='https://github.com/long2ice/alice',
|
||||||
|
license='MIT License',
|
||||||
|
packages=find_packages(include=['alice*']),
|
||||||
|
include_package_data=True,
|
||||||
|
zip_safe=True,
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': ['alice = alice.cli:cli'],
|
||||||
|
},
|
||||||
|
platforms='any',
|
||||||
|
keywords=(
|
||||||
|
'migrations Tortoise-ORM mysql'
|
||||||
|
),
|
||||||
|
install_requires=requirements(),
|
||||||
|
)
|
@ -2,17 +2,17 @@ from asynctest import TestCase
|
|||||||
from tortoise import Tortoise
|
from tortoise import Tortoise
|
||||||
|
|
||||||
from alice.backends.mysql import MysqlDDL
|
from alice.backends.mysql import MysqlDDL
|
||||||
from tests.models import Category
|
from alice.migrate import Migrate
|
||||||
|
|
||||||
TORTOISE_ORM = {
|
TORTOISE_ORM = {
|
||||||
'connections': {
|
'connections': {
|
||||||
'default': 'mysql://root:123456@127.0.0.1:3306/test'
|
'default': 'mysql://root:123456@127.0.0.1:3306/test',
|
||||||
},
|
},
|
||||||
'apps': {
|
'apps': {
|
||||||
'models': {
|
'models': {
|
||||||
'models': ['tests.models'],
|
'models': ['tests.models'],
|
||||||
'default_connection': 'default',
|
'default_connection': 'default',
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ class DBTestCase(TestCase):
|
|||||||
await Tortoise.init(config=TORTOISE_ORM)
|
await Tortoise.init(config=TORTOISE_ORM)
|
||||||
self.client = Tortoise.get_connection('default')
|
self.client = Tortoise.get_connection('default')
|
||||||
self.ddl = MysqlDDL(self.client)
|
self.ddl = MysqlDDL(self.client)
|
||||||
|
self.migrate = Migrate(ddl=self.ddl)
|
||||||
|
|
||||||
async def tearDown(self) -> None:
|
async def tearDown(self) -> None:
|
||||||
await Tortoise.close_connections()
|
await Tortoise.close_connections()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from tests.backends.mysql import DBTestCase
|
from tests.backends.mysql import DBTestCase
|
||||||
from tests.models import Category, User
|
from tests.models import Category
|
||||||
|
|
||||||
|
|
||||||
class TestDDL(DBTestCase):
|
class TestDDL(DBTestCase):
|
||||||
@ -47,7 +47,3 @@ class TestDDL(DBTestCase):
|
|||||||
def test_drop_fk(self):
|
def test_drop_fk(self):
|
||||||
ret = self.ddl.drop_fk(Category, Category._meta.fields_map.get('user'))
|
ret = self.ddl.drop_fk(Category, Category._meta.fields_map.get('user'))
|
||||||
self.assertEqual(ret, "ALTER TABLE category DROP FOREIGN KEY fk_category_user_366ffa6f")
|
self.assertEqual(ret, "ALTER TABLE category DROP FOREIGN KEY fk_category_user_366ffa6f")
|
||||||
|
|
||||||
async def test_aa(self):
|
|
||||||
user = await User.get(username='test')
|
|
||||||
await user.save()
|
|
||||||
|
7
tests/backends/mysql/test_migrate.py
Normal file
7
tests/backends/mysql/test_migrate.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from tests.backends.mysql import DBTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestMigrate(DBTestCase):
|
||||||
|
async def test_migrate(self):
|
||||||
|
self.migrate.diff_models_module('tests.models', 'tests.new_models')
|
||||||
|
print(self.migrate.operators)
|
69
tests/new_models.py
Normal file
69
tests/new_models.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import datetime
|
||||||
|
from enum import IntEnum
|
||||||
|
from tortoise import fields, Model
|
||||||
|
|
||||||
|
|
||||||
|
class ProductType(IntEnum):
|
||||||
|
article = 1
|
||||||
|
page = 2
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionAction(IntEnum):
|
||||||
|
create = 1
|
||||||
|
delete = 2
|
||||||
|
update = 3
|
||||||
|
read = 4
|
||||||
|
|
||||||
|
|
||||||
|
class Status(IntEnum):
|
||||||
|
on = 1
|
||||||
|
off = 0
|
||||||
|
|
||||||
|
|
||||||
|
class User(Model):
|
||||||
|
username = fields.CharField(max_length=20, unique=True)
|
||||||
|
password = fields.CharField(max_length=200)
|
||||||
|
last_login = fields.DatetimeField(description='Last Login', default=datetime.datetime.now)
|
||||||
|
is_active = fields.BooleanField(default=True, description='Is Active')
|
||||||
|
is_superuser = fields.BooleanField(default=False, description='Is SuperUser')
|
||||||
|
avatar = fields.CharField(max_length=200, default='')
|
||||||
|
intro = fields.TextField(default='')
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.pk}#{self.username}'
|
||||||
|
|
||||||
|
|
||||||
|
class Category(Model):
|
||||||
|
slug = fields.CharField(max_length=200)
|
||||||
|
name = fields.CharField(max_length=200)
|
||||||
|
user = fields.ForeignKeyField('new_models.User', description='User')
|
||||||
|
created_at = fields.DatetimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.pk}#{self.name}'
|
||||||
|
|
||||||
|
|
||||||
|
class Product(Model):
|
||||||
|
categories = fields.ManyToManyField('new_models.Category')
|
||||||
|
name = fields.CharField(max_length=50)
|
||||||
|
view_num = fields.IntField(description='View Num')
|
||||||
|
sort = fields.IntField()
|
||||||
|
is_reviewed = fields.BooleanField(description='Is Reviewed')
|
||||||
|
type = fields.IntEnumField(ProductType, description='Product Type')
|
||||||
|
image = fields.CharField(max_length=200)
|
||||||
|
body = fields.TextField()
|
||||||
|
created_at = fields.DatetimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.pk}#{self.name}'
|
||||||
|
|
||||||
|
|
||||||
|
class Config(Model):
|
||||||
|
label = fields.CharField(max_length=200)
|
||||||
|
key = fields.CharField(max_length=20)
|
||||||
|
value = fields.JSONField()
|
||||||
|
status: Status = fields.IntEnumField(Status, default=Status.on)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.pk}#{self.label}'
|
Loading…
x
Reference in New Issue
Block a user