add ddl
This commit is contained in:
parent
75e7a46e85
commit
4e7e1626aa
19
Makefile
Normal file
19
Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
checkfiles = alice/ examples/ tests/ conftest.py
|
||||||
|
black_opts = -l 100 -t py38
|
||||||
|
py_warn = PYTHONDEVMODE=1
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Alice development makefile"
|
||||||
|
@echo
|
||||||
|
@echo "usage: make <target>"
|
||||||
|
@echo "Targets:"
|
||||||
|
@echo " test Runs all tests"
|
||||||
|
@echo " style Auto-formats the code"
|
||||||
|
|
||||||
|
deps:
|
||||||
|
@which pip-sync > /dev/null || pip install -q pip-tools
|
||||||
|
@pip-sync tests/requirements.txt
|
||||||
|
|
||||||
|
style: deps
|
||||||
|
isort -rc $(checkfiles)
|
||||||
|
black $(black_opts) $(checkfiles)
|
@ -1,9 +1,11 @@
|
|||||||
__version__ = '0.1.0'
|
__version__ = '0.1.0'
|
||||||
|
|
||||||
|
from alice.cmd import CommandLine
|
||||||
|
|
||||||
def main():
|
|
||||||
pass
|
def main(argv):
|
||||||
|
command = CommandLine(argv)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main(None)
|
||||||
|
@ -1,36 +1,125 @@
|
|||||||
from typing import Type
|
from typing import Type, List
|
||||||
|
|
||||||
from tortoise import Model, BaseDBAsyncClient
|
from tortoise import Model, BaseDBAsyncClient, ForeignKeyFieldInstance
|
||||||
from tortoise.backends.base.schema_generator import BaseSchemaGenerator
|
from tortoise.backends.base.schema_generator import BaseSchemaGenerator
|
||||||
|
from tortoise.fields import Field, UUIDField, TextField, JSONField
|
||||||
|
|
||||||
|
|
||||||
class DDL:
|
class DDL:
|
||||||
schema_generator_cls: Type[BaseSchemaGenerator] = BaseSchemaGenerator
|
schema_generator_cls: Type[BaseSchemaGenerator] = BaseSchemaGenerator
|
||||||
|
DIALECT = "sql"
|
||||||
|
_DROP_TABLE_TEMPLATE = 'DROP TABLE {table_name} IF EXISTS'
|
||||||
|
_ADD_COLUMN_TEMPLATE = 'ALTER TABLE {table_name} ADD {column}'
|
||||||
|
_DROP_COLUMN_TEMPLATE = 'ALTER TABLE {table_name} DROP COLUMN {column_name}'
|
||||||
|
_ADD_INDEX_TEMPLATE = 'ALTER TABLE {table_name} ADD {unique} INDEX {index_name} ({column_names})'
|
||||||
|
_DROP_INDEX_TEMPLATE = 'ALTER TABLE {table_name} DROP INDEX {index_name}'
|
||||||
|
_ADD_FK_TEMPLATE = 'ALTER TABLE {table_name} ADD CONSTRAINT `{fk_name}` FOREIGN KEY (`{db_column}`) REFERENCES `{table}` (`{field}`) ON DELETE {on_delete}'
|
||||||
|
_DROP_FK_TEMPLATE = 'ALTER TABLE {table_name} DROP FOREIGN KEY {fk_name}'
|
||||||
|
|
||||||
def __init__(self, client: "BaseDBAsyncClient", model: "Type[Model]"):
|
def __init__(self, client: "BaseDBAsyncClient"):
|
||||||
self.model = model
|
self.client = client
|
||||||
self.schema_generator = self.schema_generator_cls(client)
|
self.schema_generator = self.schema_generator_cls(client)
|
||||||
|
|
||||||
def create_table(self):
|
def create_table(self, model: "Type[Model]"):
|
||||||
return self.schema_generator._get_table_sql(self.model, True)['table_creation_string']
|
return self.schema_generator._get_table_sql(model, True)['table_creation_string']
|
||||||
|
|
||||||
def drop_table(self):
|
def drop_table(self, model: "Type[Model]"):
|
||||||
return f'drop table {self.model._meta.db_table}'
|
return self._DROP_TABLE_TEMPLATE.format(
|
||||||
|
table_name=model._meta.db_table
|
||||||
|
)
|
||||||
|
|
||||||
def add_column(self):
|
def add_column(self, model: "Type[Model]", field_object: Field):
|
||||||
raise NotImplementedError()
|
db_table = model._meta.db_table
|
||||||
|
default = field_object.default
|
||||||
|
db_column = field_object.model_field_name
|
||||||
|
auto_now_add = getattr(field_object, "auto_now_add", False)
|
||||||
|
auto_now = getattr(field_object, "auto_now", False)
|
||||||
|
if default is not None or auto_now or auto_now_add:
|
||||||
|
if callable(default) or isinstance(field_object, (UUIDField, TextField, JSONField)):
|
||||||
|
default = ""
|
||||||
|
else:
|
||||||
|
default = field_object.to_db_value(default, model)
|
||||||
|
try:
|
||||||
|
default = self.schema_generator._column_default_generator(
|
||||||
|
db_table,
|
||||||
|
db_column,
|
||||||
|
self.schema_generator._escape_default_value(default),
|
||||||
|
auto_now_add,
|
||||||
|
auto_now,
|
||||||
|
)
|
||||||
|
except NotImplementedError:
|
||||||
|
default = ""
|
||||||
|
else:
|
||||||
|
default = ""
|
||||||
|
return self._ADD_COLUMN_TEMPLATE.format(
|
||||||
|
table_name=db_table,
|
||||||
|
column=self.schema_generator._create_string(
|
||||||
|
db_column=field_object.model_field_name,
|
||||||
|
field_type=field_object.get_for_dialect(self.DIALECT, "SQL_TYPE"),
|
||||||
|
nullable="NOT NULL" if not field_object.null else "",
|
||||||
|
unique="UNIQUE" if field_object.unique else "",
|
||||||
|
comment=self.schema_generator._column_comment_generator(
|
||||||
|
table=db_table, column=field_object.model_field_name, comment=field_object.description
|
||||||
|
)
|
||||||
|
if field_object.description else "",
|
||||||
|
is_primary_key=field_object.pk,
|
||||||
|
default=default,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def drop_column(self):
|
def drop_column(self, model: "Type[Model]", column_name: str):
|
||||||
raise NotImplementedError()
|
return self._DROP_COLUMN_TEMPLATE.format(
|
||||||
|
table_name=model._meta.db_table,
|
||||||
|
column_name=column_name
|
||||||
|
)
|
||||||
|
|
||||||
def add_index(self):
|
def add_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
raise NotImplementedError()
|
return self._ADD_INDEX_TEMPLATE.format(
|
||||||
|
unique='UNIQUE' if unique else '',
|
||||||
|
index_name=self.schema_generator._generate_index_name("idx" if not unique else "uid", model,
|
||||||
|
field_names),
|
||||||
|
table_name=model._meta.db_table,
|
||||||
|
column_names=", ".join([self.schema_generator.quote(f) for f in field_names]),
|
||||||
|
)
|
||||||
|
|
||||||
def drop_index(self):
|
def drop_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
raise NotImplementedError()
|
return self._DROP_INDEX_TEMPLATE.format(
|
||||||
|
index_name=self.schema_generator._generate_index_name("idx" if not unique else "uid", model,
|
||||||
|
field_names),
|
||||||
|
table_name=model._meta.db_table,
|
||||||
|
)
|
||||||
|
|
||||||
def add_fk(self):
|
def add_fk(self, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
||||||
raise NotImplementedError()
|
db_table = model._meta.db_table
|
||||||
|
to_field_name = field.to_field_instance.source_field
|
||||||
|
if not to_field_name:
|
||||||
|
to_field_name = field.to_field_instance.model_field_name
|
||||||
|
|
||||||
def drop_fk(self):
|
fk_name = self.schema_generator._generate_fk_name(
|
||||||
raise NotImplementedError()
|
from_table=db_table,
|
||||||
|
from_field=field.model_field_name,
|
||||||
|
to_table=field.related_model._meta.db_table,
|
||||||
|
to_field=to_field_name
|
||||||
|
)
|
||||||
|
return self._ADD_FK_TEMPLATE.format(
|
||||||
|
table_name=db_table,
|
||||||
|
fk_name=fk_name,
|
||||||
|
db_column=field.model_field_name,
|
||||||
|
table=field.related_model._meta.db_table,
|
||||||
|
field=to_field_name,
|
||||||
|
on_delete=field.on_delete,
|
||||||
|
)
|
||||||
|
|
||||||
|
def drop_fk(self, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
||||||
|
to_field_name = field.to_field_instance.source_field
|
||||||
|
if not to_field_name:
|
||||||
|
to_field_name = field.to_field_instance.model_field_name
|
||||||
|
return self._DROP_FK_TEMPLATE.format(
|
||||||
|
table_name=model._meta.db_table,
|
||||||
|
fk_name=self.schema_generator._generate_fk_name(
|
||||||
|
from_table=model._meta.db_table,
|
||||||
|
from_field=field.model_field_name,
|
||||||
|
to_table=field.related_model._meta.db_table,
|
||||||
|
to_field=to_field_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -5,24 +5,4 @@ from alice.backends import DDL
|
|||||||
|
|
||||||
class MysqlDDL(DDL):
|
class MysqlDDL(DDL):
|
||||||
schema_generator_cls = MySQLSchemaGenerator
|
schema_generator_cls = MySQLSchemaGenerator
|
||||||
|
DIALECT = "mysql"
|
||||||
def drop_table(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_column(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def drop_column(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_index(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def drop_index(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_fk(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def drop_fk(self):
|
|
||||||
pass
|
|
||||||
|
55
poetry.lock
generated
55
poetry.lock
generated
@ -67,6 +67,19 @@ idna = ["idna (>=2.1)"]
|
|||||||
pep8test = ["flake8", "flake8-import-order", "pep8-naming"]
|
pep8test = ["flake8", "flake8-import-order", "pep8-naming"]
|
||||||
test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"]
|
test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "the modular source code checker: pep8 pyflakes and co"
|
||||||
|
name = "flake8"
|
||||||
|
optional = false
|
||||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||||
|
version = "3.8.1"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
mccabe = ">=0.6.0,<0.7.0"
|
||||||
|
pycodestyle = ">=2.6.0a1,<2.7.0"
|
||||||
|
pyflakes = ">=2.2.0,<2.3.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Simple module to parse ISO 8601 dates"
|
description = "Simple module to parse ISO 8601 dates"
|
||||||
@ -76,6 +89,22 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "McCabe checker, plugin for flake8"
|
||||||
|
name = "mccabe"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "0.6.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "Python style guide checker"
|
||||||
|
name = "pycodestyle"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
version = "2.6.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "C parser in Python"
|
description = "C parser in Python"
|
||||||
@ -84,6 +113,14 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "2.20"
|
version = "2.20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "passive checker of Python programs"
|
||||||
|
name = "pyflakes"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
version = "2.2.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Pure Python MySQL Driver"
|
description = "Pure Python MySQL Driver"
|
||||||
@ -154,7 +191,7 @@ python-versions = "*"
|
|||||||
version = "3.7.4.2"
|
version = "3.7.4.2"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "e8e49dcc243fcd3a31fd60ee5a637af7b265c4fea2078a48d2bd7f10794fe323"
|
content-hash = "0b4c3c6eb6ed2e84b03745542ebc42dc53e6e5466bea0c4eee991bcd063a6ef3"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@ -224,15 +261,31 @@ cryptography = [
|
|||||||
{file = "cryptography-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5"},
|
{file = "cryptography-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5"},
|
||||||
{file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"},
|
{file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"},
|
||||||
]
|
]
|
||||||
|
flake8 = [
|
||||||
|
{file = "flake8-3.8.1-py2.py3-none-any.whl", hash = "sha256:6c1193b0c3f853ef763969238f6c81e9e63ace9d024518edc020d5f1d6d93195"},
|
||||||
|
{file = "flake8-3.8.1.tar.gz", hash = "sha256:ea6623797bf9a52f4c9577d780da0bb17d65f870213f7b5bcc9fca82540c31d5"},
|
||||||
|
]
|
||||||
iso8601 = [
|
iso8601 = [
|
||||||
{file = "iso8601-0.1.12-py2.py3-none-any.whl", hash = "sha256:210e0134677cc0d02f6028087fee1df1e1d76d372ee1db0bf30bf66c5c1c89a3"},
|
{file = "iso8601-0.1.12-py2.py3-none-any.whl", hash = "sha256:210e0134677cc0d02f6028087fee1df1e1d76d372ee1db0bf30bf66c5c1c89a3"},
|
||||||
{file = "iso8601-0.1.12-py3-none-any.whl", hash = "sha256:bbbae5fb4a7abfe71d4688fd64bff70b91bbd74ef6a99d964bab18f7fdf286dd"},
|
{file = "iso8601-0.1.12-py3-none-any.whl", hash = "sha256:bbbae5fb4a7abfe71d4688fd64bff70b91bbd74ef6a99d964bab18f7fdf286dd"},
|
||||||
{file = "iso8601-0.1.12.tar.gz", hash = "sha256:49c4b20e1f38aa5cf109ddcd39647ac419f928512c869dc01d5c7098eddede82"},
|
{file = "iso8601-0.1.12.tar.gz", hash = "sha256:49c4b20e1f38aa5cf109ddcd39647ac419f928512c869dc01d5c7098eddede82"},
|
||||||
]
|
]
|
||||||
|
mccabe = [
|
||||||
|
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||||
|
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||||
|
]
|
||||||
|
pycodestyle = [
|
||||||
|
{file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
|
||||||
|
{file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
|
||||||
|
]
|
||||||
pycparser = [
|
pycparser = [
|
||||||
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||||
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||||
]
|
]
|
||||||
|
pyflakes = [
|
||||||
|
{file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
|
||||||
|
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
|
||||||
|
]
|
||||||
pymysql = [
|
pymysql = [
|
||||||
{file = "PyMySQL-0.9.2-py2.py3-none-any.whl", hash = "sha256:95f057328357e0e13a30e67857a8c694878b0175797a9a203ee7adbfb9b1ec5f"},
|
{file = "PyMySQL-0.9.2-py2.py3-none-any.whl", hash = "sha256:95f057328357e0e13a30e67857a8c694878b0175797a9a203ee7adbfb9b1ec5f"},
|
||||||
{file = "PyMySQL-0.9.2.tar.gz", hash = "sha256:9ec760cbb251c158c19d6c88c17ca00a8632bac713890e465b2be01fdc30713f"},
|
{file = "PyMySQL-0.9.2.tar.gz", hash = "sha256:9ec760cbb251c158c19d6c88c17ca00a8632bac713890e465b2be01fdc30713f"},
|
||||||
|
@ -12,6 +12,7 @@ aiomysql = "*"
|
|||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
taskipy = "*"
|
taskipy = "*"
|
||||||
asynctest = "*"
|
asynctest = "*"
|
||||||
|
flake8 = "*"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry>=0.12"]
|
requires = ["poetry>=0.12"]
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
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='')
|
|
||||||
created_at = fields.DatetimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f'{self.pk}#{self.username}'
|
|
||||||
|
|
||||||
|
|
||||||
class Category(Model):
|
|
||||||
slug = fields.CharField(max_length=200)
|
|
||||||
name = fields.CharField(max_length=200)
|
|
||||||
created_at = fields.DatetimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f'{self.pk}#{self.name}'
|
|
||||||
|
|
||||||
|
|
||||||
class Product(Model):
|
|
||||||
categories = fields.ManyToManyField('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}'
|
|
0
tests/backends/__init__.py
Normal file
0
tests/backends/__init__.py
Normal file
@ -2,28 +2,26 @@ 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 import User
|
from tests.models import Category
|
||||||
|
|
||||||
TORTOISE_ORM = {
|
TORTOISE_ORM = {
|
||||||
'connections': {
|
'connections': {
|
||||||
'default': 'mysql://root:123456@127.0.0.1:3306/fastapi-admin'
|
'default': 'mysql://root:123456@127.0.0.1:3306/test'
|
||||||
},
|
},
|
||||||
'apps': {
|
'apps': {
|
||||||
'models': {
|
'models': {
|
||||||
'models': ['tests'],
|
'models': ['tests.models'],
|
||||||
'default_connection': 'default',
|
'default_connection': 'default',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestMysql(TestCase):
|
class DBTestCase(TestCase):
|
||||||
async def setUp(self) -> None:
|
async def setUp(self) -> None:
|
||||||
await Tortoise.init(config=TORTOISE_ORM)
|
await Tortoise.init(config=TORTOISE_ORM)
|
||||||
|
self.client = Tortoise.get_connection('default')
|
||||||
async def test_create_table(self):
|
self.ddl = MysqlDDL(self.client)
|
||||||
ddl = MysqlDDL(Tortoise.get_connection('default'), User)
|
|
||||||
print(ddl.create_table())
|
|
||||||
|
|
||||||
async def tearDown(self) -> None:
|
async def tearDown(self) -> None:
|
||||||
await Tortoise.close_connections()
|
await Tortoise.close_connections()
|
53
tests/backends/mysql/test_ddl.py
Normal file
53
tests/backends/mysql/test_ddl.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from tests.backends.mysql import DBTestCase
|
||||||
|
from tests.models import Category, User
|
||||||
|
|
||||||
|
|
||||||
|
class TestDDL(DBTestCase):
|
||||||
|
def test_create_table(self):
|
||||||
|
ret = self.ddl.create_table(Category)
|
||||||
|
self.assertEqual(
|
||||||
|
ret, """CREATE TABLE IF NOT EXISTS `category` (
|
||||||
|
`id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
`slug` VARCHAR(200) NOT NULL,
|
||||||
|
`name` VARCHAR(200) NOT NULL,
|
||||||
|
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||||
|
`user_id` INT NOT NULL COMMENT 'User',
|
||||||
|
CONSTRAINT `fk_category_user_e2e3874c` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4;""")
|
||||||
|
|
||||||
|
def test_drop_table(self):
|
||||||
|
ret = self.ddl.drop_table(Category)
|
||||||
|
self.assertEqual(ret, "DROP TABLE category IF EXISTS")
|
||||||
|
|
||||||
|
def test_add_column(self):
|
||||||
|
ret = self.ddl.add_column(Category, Category._meta.fields_map.get('name'))
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category ADD `name` VARCHAR(200) NOT NULL")
|
||||||
|
|
||||||
|
def test_drop_column(self):
|
||||||
|
ret = self.ddl.drop_column(Category, 'name')
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category DROP COLUMN name")
|
||||||
|
|
||||||
|
def test_add_index(self):
|
||||||
|
ret = self.ddl.add_index(Category, ['name'])
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category ADD INDEX idx_category_name_8b0cb9 (`name`)")
|
||||||
|
ret = self.ddl.add_index(Category, ['name'], True)
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category ADD UNIQUE INDEX uid_category_name_8b0cb9 (`name`)")
|
||||||
|
|
||||||
|
def test_drop_index(self):
|
||||||
|
ret = self.ddl.drop_index(Category, ['name'])
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category DROP INDEX idx_category_name_8b0cb9")
|
||||||
|
ret = self.ddl.drop_index(Category, ['name'], True)
|
||||||
|
self.assertEqual(ret, "ALTER TABLE category DROP INDEX uid_category_name_8b0cb9")
|
||||||
|
|
||||||
|
def test_add_fk(self):
|
||||||
|
ret = self.ddl.add_fk(Category, Category._meta.fields_map.get('user'))
|
||||||
|
self.assertEqual(ret,
|
||||||
|
"ALTER TABLE category ADD CONSTRAINT `fk_category_user_366ffa6f` FOREIGN KEY (`user`) REFERENCES `user` (`id`) ON DELETE CASCADE")
|
||||||
|
|
||||||
|
def test_drop_fk(self):
|
||||||
|
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")
|
||||||
|
|
||||||
|
async def test_aa(self):
|
||||||
|
user = await User.get(username='test')
|
||||||
|
await user.save()
|
70
tests/models.py
Normal file
70
tests/models.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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='')
|
||||||
|
created_at = fields.DatetimeField(auto_now_add=True)
|
||||||
|
updated_at = fields.DatetimeField(auto_now=True)
|
||||||
|
|
||||||
|
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('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('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