now alice is basic worked
This commit is contained in:
parent
a79f5c1dc6
commit
a99a06e563
22
.github/workflows/pypi.yml
vendored
Normal file
22
.github/workflows/pypi.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: pypi
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'alice/__init__.py'
|
||||||
|
- '.github/workflows/pypi.yml'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Build dists
|
||||||
|
run: |
|
||||||
|
python3 setup.py sdist
|
||||||
|
- name: Pypi Publish
|
||||||
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
|
with:
|
||||||
|
user: __token__
|
||||||
|
password: ${{ secrets.pypi_password }}
|
@ -0,0 +1,10 @@
|
|||||||
|
=========
|
||||||
|
ChangeLog
|
||||||
|
=========
|
||||||
|
|
||||||
|
|
||||||
|
0.1
|
||||||
|
===
|
||||||
|
0.1.1
|
||||||
|
-----
|
||||||
|
- Now alice is basic worked.
|
35
Makefile
35
Makefile
@ -7,13 +7,44 @@ help:
|
|||||||
@echo
|
@echo
|
||||||
@echo "usage: make <target>"
|
@echo "usage: make <target>"
|
||||||
@echo "Targets:"
|
@echo "Targets:"
|
||||||
|
@echo " up Updates dev/test dependencies"
|
||||||
|
@echo " deps Ensure dev/test dependencies are installed"
|
||||||
|
@echo " check Checks that build is sane"
|
||||||
|
@echo " lint Reports all linter violations"
|
||||||
@echo " test Runs all tests"
|
@echo " test Runs all tests"
|
||||||
@echo " style Auto-formats the code"
|
@echo " style Auto-formats the code"
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
@which pip-sync > /dev/null || pip install -q pip-tools
|
@which pip-sync > /dev/null || pip install -q pip-tools
|
||||||
@pip install -r requirements-dev.txt
|
@pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
up:
|
||||||
|
CUSTOM_COMPILE_COMMAND="make up" pip-compile -o requirements-dev.txt -U
|
||||||
|
sed -i "s/^-e .*/-e ./" requirements.txt
|
||||||
|
|
||||||
style: deps
|
style: deps
|
||||||
isort -rc $(checkfiles)
|
isort -rc $(checkfiles)
|
||||||
black $(black_opts) $(checkfiles)
|
black $(black_opts) $(checkfiles)
|
||||||
|
|
||||||
|
check: deps
|
||||||
|
ifneq ($(shell which black),)
|
||||||
|
black --check $(black_opts) $(checkfiles) || (echo "Please run 'make style' to auto-fix style issues" && false)
|
||||||
|
endif
|
||||||
|
flake8 $(checkfiles)
|
||||||
|
mypy $(checkfiles)
|
||||||
|
pylint -d C,W,R $(checkfiles)
|
||||||
|
bandit -r $(checkfiles)
|
||||||
|
python setup.py check -mrs
|
||||||
|
|
||||||
|
lint: deps
|
||||||
|
ifneq ($(shell which black),)
|
||||||
|
black --check $(black_opts) $(checkfiles) || (echo "Please run 'make style' to auto-fix style issues" && false)
|
||||||
|
endif
|
||||||
|
flake8 $(checkfiles)
|
||||||
|
mypy $(checkfiles)
|
||||||
|
pylint $(checkfiles)
|
||||||
|
bandit -r $(checkfiles)
|
||||||
|
python setup.py check -mrs
|
||||||
|
|
||||||
|
test: deps
|
||||||
|
$(py_warn) py.test
|
120
README.rst
120
README.rst
@ -0,0 +1,120 @@
|
|||||||
|
=====
|
||||||
|
Alice
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/alice.svg?style=flat
|
||||||
|
:target: https://pypi.python.org/pypi/alice
|
||||||
|
.. image:: https://img.shields.io/github/license/long2ice/alice
|
||||||
|
:target: https://github.com/long2ice/alice
|
||||||
|
.. image:: https://github.com/long2ice/alice/workflows/pypi/badge.svg
|
||||||
|
:target: https://github.com/long2ice/alice/actions?query=workflow:pypi
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
|
Tortoise-ORM is the best asyncio ORM now, but it lacks a database migrations tool like alembic for SQLAlchemy, or Django ORM with it's own migrations tool.
|
||||||
|
|
||||||
|
This project aim to be a best migrations tool for Tortoise-ORM and which written by one of contributors of Tortoise-ORM.
|
||||||
|
|
||||||
|
Install
|
||||||
|
=======
|
||||||
|
|
||||||
|
Just install from pypi:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ pip install alice
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice -h
|
||||||
|
|
||||||
|
Usage: alice [OPTIONS] COMMAND [ARGS]...
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--config TEXT Tortoise-ORM config module, will auto read config dict variable
|
||||||
|
from it. [default: settings]
|
||||||
|
--tortoise-orm TEXT Tortoise-ORM config dict variable. [default:
|
||||||
|
TORTOISE_ORM]
|
||||||
|
--location TEXT Migrate store location. [default: ./migrations]
|
||||||
|
--app TEXT Tortoise-ORM app name. [default: models]
|
||||||
|
-h, --help Show this message and exit.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
downgrade Downgrade to previous version.
|
||||||
|
heads Show current available heads in migrate location.
|
||||||
|
history List all migrate items.
|
||||||
|
init Init migrate location and generate schema, you must exec first.
|
||||||
|
migrate Generate migrate changes file.
|
||||||
|
upgrade Upgrade to latest version.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
Init schema and migrate location
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql init
|
||||||
|
|
||||||
|
Success create migrate location ./migrations/models
|
||||||
|
Success init for app "models"
|
||||||
|
|
||||||
|
Update models and make migrate
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql migrate --name drop_column
|
||||||
|
|
||||||
|
Success migrate 1_202029051520102929_drop_column.json
|
||||||
|
|
||||||
|
Format of migrate filename is {version}_{datetime}_{name|update}.json
|
||||||
|
|
||||||
|
Upgrade to latest version
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql upgrade
|
||||||
|
|
||||||
|
Success upgrade 1_202029051520102929_drop_column.json
|
||||||
|
|
||||||
|
Now your db is migrated to latest.
|
||||||
|
|
||||||
|
Downgrade to previous version
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql downgrade
|
||||||
|
|
||||||
|
Success downgrade 1_202029051520102929_drop_column.json
|
||||||
|
|
||||||
|
Now your db rollback to previous version.
|
||||||
|
|
||||||
|
Show history
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql history
|
||||||
|
|
||||||
|
1_202029051520102929_drop_column.json
|
||||||
|
|
||||||
|
Show heads to be migrated
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
$ alice --config tests.backends.mysql heads
|
||||||
|
|
||||||
|
1_202029051520102929_drop_column.json
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
This project is licensed under the `MIT <https://github.com/long2ice/alice/blob/master/LICENSE>`_ License.
|
@ -1 +1 @@
|
|||||||
__version__ = "0.1.0"
|
__version__ = "0.1.1"
|
||||||
|
14
alice/cli.py
14
alice/cli.py
@ -5,7 +5,7 @@ import sys
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
import asyncclick as click
|
import asyncclick as click
|
||||||
from asyncclick import BadOptionUsage, BadParameter, Context, UsageError
|
from asyncclick import BadOptionUsage, Context, UsageError
|
||||||
from tortoise import Tortoise, generate_schema_for_client
|
from tortoise import Tortoise, generate_schema_for_client
|
||||||
|
|
||||||
from alice.migrate import Migrate
|
from alice.migrate import Migrate
|
||||||
@ -23,7 +23,7 @@ class Color(str, Enum):
|
|||||||
"--config",
|
"--config",
|
||||||
default="settings",
|
default="settings",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
help="Tortoise-ORM config module, will read config variable from it.",
|
help="Tortoise-ORM config module, will auto read dict config variable from it.",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--tortoise-orm",
|
"--tortoise-orm",
|
||||||
@ -44,10 +44,14 @@ async def cli(ctx: Context, config, tortoise_orm, location, app):
|
|||||||
raise BadOptionUsage(ctx=ctx, message=f'No module named "{config}"', option_name="--config")
|
raise BadOptionUsage(ctx=ctx, message=f'No module named "{config}"', option_name="--config")
|
||||||
config = getattr(config_module, tortoise_orm, None)
|
config = getattr(config_module, tortoise_orm, None)
|
||||||
if not config:
|
if not config:
|
||||||
raise BadOptionUsage(f'Can\'t get "{tortoise_orm}" from module "{config_module}"', ctx=ctx)
|
raise BadOptionUsage(
|
||||||
|
option_name="--config",
|
||||||
|
message=f'Can\'t get "{tortoise_orm}" from module "{config_module}"',
|
||||||
|
ctx=ctx,
|
||||||
|
)
|
||||||
|
|
||||||
if app not in config.get("apps").keys():
|
if app not in config.get("apps").keys():
|
||||||
raise BadOptionUsage(f'No app found in "{config}"', ctx=ctx)
|
raise BadOptionUsage(option_name="--config", message=f'No app found in "{config}"', ctx=ctx)
|
||||||
|
|
||||||
ctx.obj["config"] = config
|
ctx.obj["config"] = config
|
||||||
ctx.obj["location"] = location
|
ctx.obj["location"] = location
|
||||||
@ -142,7 +146,7 @@ def history(ctx):
|
|||||||
|
|
||||||
|
|
||||||
@cli.command(
|
@cli.command(
|
||||||
help="Init migrate location and generate schema, you must exec first before other cmd."
|
help="Init migrate location and generate schema, you must exec first."
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--safe",
|
"--safe",
|
||||||
|
@ -2,10 +2,10 @@ from typing import List, Type
|
|||||||
|
|
||||||
from tortoise import BaseDBAsyncClient, ForeignKeyFieldInstance, Model
|
from tortoise import BaseDBAsyncClient, ForeignKeyFieldInstance, Model
|
||||||
from tortoise.backends.base.schema_generator import BaseSchemaGenerator
|
from tortoise.backends.base.schema_generator import BaseSchemaGenerator
|
||||||
from tortoise.fields import Field, JSONField, TextField, UUIDField
|
from tortoise.fields import Field
|
||||||
|
|
||||||
|
|
||||||
class DDL:
|
class BaseDDL:
|
||||||
schema_generator_cls: Type[BaseSchemaGenerator] = BaseSchemaGenerator
|
schema_generator_cls: Type[BaseSchemaGenerator] = BaseSchemaGenerator
|
||||||
DIALECT = "sql"
|
DIALECT = "sql"
|
||||||
_DROP_TABLE_TEMPLATE = "DROP TABLE {table_name} IF EXISTS"
|
_DROP_TABLE_TEMPLATE = "DROP TABLE {table_name} IF EXISTS"
|
||||||
@ -23,108 +23,25 @@ class DDL:
|
|||||||
self.schema_generator = self.schema_generator_cls(client)
|
self.schema_generator = self.schema_generator_cls(client)
|
||||||
|
|
||||||
def create_table(self, model: "Type[Model]"):
|
def create_table(self, model: "Type[Model]"):
|
||||||
return self.schema_generator._get_table_sql(model, True)["table_creation_string"]
|
raise NotImplementedError
|
||||||
|
|
||||||
def drop_table(self, model: "Type[Model]"):
|
def drop_table(self, model: "Type[Model]"):
|
||||||
return self._DROP_TABLE_TEMPLATE.format(table_name=model._meta.db_table)
|
raise NotImplementedError
|
||||||
|
|
||||||
def add_column(self, model: "Type[Model]", field_object: Field):
|
def add_column(self, model: "Type[Model]", field_object: Field):
|
||||||
db_table = model._meta.db_table
|
raise NotImplementedError
|
||||||
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, model: "Type[Model]", column_name: str):
|
def drop_column(self, model: "Type[Model]", column_name: str):
|
||||||
return self._DROP_COLUMN_TEMPLATE.format(
|
raise NotImplementedError
|
||||||
table_name=model._meta.db_table, column_name=column_name
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
def add_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
return self._ADD_INDEX_TEMPLATE.format(
|
raise NotImplementedError
|
||||||
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, model: "Type[Model]", field_names: List[str], unique=False):
|
def drop_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
return self._DROP_INDEX_TEMPLATE.format(
|
raise NotImplementedError
|
||||||
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, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
def add_fk(self, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
||||||
db_table = model._meta.db_table
|
raise NotImplementedError
|
||||||
to_field_name = field.to_field_instance.source_field
|
|
||||||
if not to_field_name:
|
|
||||||
to_field_name = field.to_field_instance.model_field_name
|
|
||||||
|
|
||||||
fk_name = self.schema_generator._generate_fk_name(
|
|
||||||
from_table=db_table,
|
|
||||||
from_field=field.source_field or field.model_field_name + "_id",
|
|
||||||
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.source_field or field.model_field_name + "_id",
|
|
||||||
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):
|
def drop_fk(self, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
||||||
to_field_name = field.to_field_instance.source_field
|
raise NotImplementedError
|
||||||
if not to_field_name:
|
|
||||||
to_field_name = field.to_field_instance.model_field_name
|
|
||||||
db_table = model._meta.db_table
|
|
||||||
return self._DROP_FK_TEMPLATE.format(
|
|
||||||
table_name=db_table,
|
|
||||||
fk_name=self.schema_generator._generate_fk_name(
|
|
||||||
from_table=db_table,
|
|
||||||
from_field=field.source_field or field.model_field_name + "_id",
|
|
||||||
to_table=field.related_model._meta.db_table,
|
|
||||||
to_field=to_field_name,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
@ -1,8 +1,120 @@
|
|||||||
|
from typing import List, Type
|
||||||
|
|
||||||
|
from tortoise import ForeignKeyFieldInstance, Model
|
||||||
from tortoise.backends.mysql.schema_generator import MySQLSchemaGenerator
|
from tortoise.backends.mysql.schema_generator import MySQLSchemaGenerator
|
||||||
|
from tortoise.fields import Field, JSONField, TextField, UUIDField
|
||||||
|
|
||||||
from alice.ddl import DDL
|
from alice.ddl import BaseDDL
|
||||||
|
|
||||||
|
|
||||||
class MysqlDDL(DDL):
|
class MysqlDDL(BaseDDL):
|
||||||
schema_generator_cls = MySQLSchemaGenerator
|
schema_generator_cls = MySQLSchemaGenerator
|
||||||
DIALECT = MySQLSchemaGenerator.DIALECT
|
DIALECT = MySQLSchemaGenerator.DIALECT
|
||||||
|
|
||||||
|
def create_table(self, model: "Type[Model]"):
|
||||||
|
return self.schema_generator._get_table_sql(model, True)["table_creation_string"]
|
||||||
|
|
||||||
|
def drop_table(self, model: "Type[Model]"):
|
||||||
|
return self._DROP_TABLE_TEMPLATE.format(table_name=model._meta.db_table)
|
||||||
|
|
||||||
|
def add_column(self, model: "Type[Model]", field_object: Field):
|
||||||
|
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_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, model: "Type[Model]", column_name: str):
|
||||||
|
return self._DROP_COLUMN_TEMPLATE.format(
|
||||||
|
table_name=model._meta.db_table, column_name=column_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_index(self, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
|
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, model: "Type[Model]", field_names: List[str], unique=False):
|
||||||
|
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, model: "Type[Model]", field: ForeignKeyFieldInstance):
|
||||||
|
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
|
||||||
|
|
||||||
|
db_column = field.source_field or field.model_field_name + "_id"
|
||||||
|
fk_name = self.schema_generator._generate_fk_name(
|
||||||
|
from_table=db_table,
|
||||||
|
from_field=db_column,
|
||||||
|
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=db_column,
|
||||||
|
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
|
||||||
|
db_table = model._meta.db_table
|
||||||
|
return self._DROP_FK_TEMPLATE.format(
|
||||||
|
table_name=db_table,
|
||||||
|
fk_name=self.schema_generator._generate_fk_name(
|
||||||
|
from_table=db_table,
|
||||||
|
from_field=field.source_field or field.model_field_name + "_id",
|
||||||
|
to_table=field.related_model._meta.db_table,
|
||||||
|
to_field=to_field_name,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@ -9,7 +9,7 @@ from tortoise import BackwardFKRelation, ForeignKeyFieldInstance, Model, Tortois
|
|||||||
from tortoise.backends.mysql.schema_generator import MySQLSchemaGenerator
|
from tortoise.backends.mysql.schema_generator import MySQLSchemaGenerator
|
||||||
from tortoise.fields import Field
|
from tortoise.fields import Field
|
||||||
|
|
||||||
from alice.ddl import DDL
|
from alice.ddl import BaseDDL
|
||||||
from alice.ddl.mysql import MysqlDDL
|
from alice.ddl.mysql import MysqlDDL
|
||||||
from alice.exceptions import ConfigurationError
|
from alice.exceptions import ConfigurationError
|
||||||
from alice.utils import get_app_connection
|
from alice.utils import get_app_connection
|
||||||
@ -21,7 +21,7 @@ class Migrate:
|
|||||||
_upgrade_fk_operators: List[str] = []
|
_upgrade_fk_operators: List[str] = []
|
||||||
_downgrade_fk_operators: List[str] = []
|
_downgrade_fk_operators: List[str] = []
|
||||||
|
|
||||||
ddl: DDL
|
ddl: BaseDDL
|
||||||
migrate_config: dict
|
migrate_config: dict
|
||||||
old_models = "old_models"
|
old_models = "old_models"
|
||||||
diff_app = "diff_models"
|
diff_app = "diff_models"
|
||||||
@ -85,6 +85,11 @@ class Migrate:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def migrate(cls, name):
|
def migrate(cls, name):
|
||||||
|
"""
|
||||||
|
diff old models and new models to generate diff content
|
||||||
|
:param name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
if not cls.migrate_config:
|
if not cls.migrate_config:
|
||||||
raise ConfigurationError("You must call init_with_old_models() first!")
|
raise ConfigurationError("You must call init_with_old_models() first!")
|
||||||
apps = Tortoise.apps
|
apps = Tortoise.apps
|
||||||
@ -103,6 +108,13 @@ class Migrate:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _add_operator(cls, operator: str, upgrade=True, fk=False):
|
def _add_operator(cls, operator: str, upgrade=True, fk=False):
|
||||||
|
"""
|
||||||
|
add operator,differentiate fk because fk is order limit
|
||||||
|
:param operator:
|
||||||
|
:param upgrade:
|
||||||
|
:param fk:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
if upgrade:
|
if upgrade:
|
||||||
if fk:
|
if fk:
|
||||||
cls._upgrade_fk_operators.append(operator)
|
cls._upgrade_fk_operators.append(operator)
|
||||||
@ -134,6 +146,13 @@ class Migrate:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_migrate_config(cls, config: dict, app: str, location: str):
|
def _get_migrate_config(cls, config: dict, app: str, location: str):
|
||||||
|
"""
|
||||||
|
generate tmp config with old models
|
||||||
|
:param config:
|
||||||
|
:param app:
|
||||||
|
:param location:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
temp_config = deepcopy(config)
|
temp_config = deepcopy(config)
|
||||||
path = os.path.join(location, app, cls.old_models)
|
path = os.path.join(location, app, cls.old_models)
|
||||||
path = path.replace("/", ".").lstrip(".")
|
path = path.replace("/", ".").lstrip(".")
|
||||||
@ -142,6 +161,13 @@ class Migrate:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def write_old_models(cls, config: dict, app: str, location: str):
|
def write_old_models(cls, config: dict, app: str, location: str):
|
||||||
|
"""
|
||||||
|
write new models to old models
|
||||||
|
:param config:
|
||||||
|
:param app:
|
||||||
|
:param location:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
old_model_files = []
|
old_model_files = []
|
||||||
models = config.get("apps").get(app).get("models")
|
models = config.get("apps").get(app).get("models")
|
||||||
for model in models:
|
for model in models:
|
||||||
@ -235,6 +261,11 @@ class Migrate:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _exclude_field(cls, field: Field):
|
def _exclude_field(cls, field: Field):
|
||||||
|
"""
|
||||||
|
exclude BackwardFKRelation
|
||||||
|
:param field:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return isinstance(field, BackwardFKRelation)
|
return isinstance(field, BackwardFKRelation)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -251,11 +282,23 @@ class Migrate:
|
|||||||
return cls.ddl.drop_column(model, field.model_field_name)
|
return cls.ddl.drop_column(model, field.model_field_name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _add_fk(cls, model: Type[Model], field: Field):
|
def _add_fk(cls, model: Type[Model], field: ForeignKeyFieldInstance):
|
||||||
|
"""
|
||||||
|
add fk
|
||||||
|
:param model:
|
||||||
|
:param field:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return cls.ddl.add_fk(model, field)
|
return cls.ddl.add_fk(model, field)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _remove_fk(cls, model: Type[Model], field: Field):
|
def _remove_fk(cls, model: Type[Model], field: ForeignKeyFieldInstance):
|
||||||
|
"""
|
||||||
|
drop fk
|
||||||
|
:param model:
|
||||||
|
:param field:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
return cls.ddl.drop_fk(model, field)
|
return cls.ddl.drop_fk(model, field)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
44
poetry.lock
generated
44
poetry.lock
generated
@ -259,7 +259,7 @@ description = "Alternative regular expression module, to replace re."
|
|||||||
name = "regex"
|
name = "regex"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "2020.5.13"
|
version = "2020.5.14"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
@ -470,27 +470,27 @@ pypika = [
|
|||||||
{file = "PyPika-0.37.6.tar.gz", hash = "sha256:64510fa36667e8bb654bdc1be5a3a77bac1dbc2f03d4848efac08e39d9cac6f5"},
|
{file = "PyPika-0.37.6.tar.gz", hash = "sha256:64510fa36667e8bb654bdc1be5a3a77bac1dbc2f03d4848efac08e39d9cac6f5"},
|
||||||
]
|
]
|
||||||
regex = [
|
regex = [
|
||||||
{file = "regex-2020.5.13-cp27-cp27m-win32.whl", hash = "sha256:d20ba8d3a844d6cd9bbe127a60cc8f61cfe2d2b77da997775639999a48fbbfe3"},
|
{file = "regex-2020.5.14-cp27-cp27m-win32.whl", hash = "sha256:e565569fc28e3ba3e475ec344d87ed3cd8ba2d575335359749298a0899fe122e"},
|
||||||
{file = "regex-2020.5.13-cp27-cp27m-win_amd64.whl", hash = "sha256:2cdf6bb8112a6b49092a9320edc823d41b21a06c8c7ac88a845b73462193f3ac"},
|
{file = "regex-2020.5.14-cp27-cp27m-win_amd64.whl", hash = "sha256:d466967ac8e45244b9dfe302bbe5e3337f8dc4dec8d7d10f5e950d83b140d33a"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9d801d3693c5a6af5a506f9905b0e6d8ae419a00054d8d80ed9d65f81fc37e1f"},
|
{file = "regex-2020.5.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:27ff7325b297fb6e5ebb70d10437592433601c423f5acf86e5bc1ee2919b9561"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:705e334ac42cf807f33d4daafe58da33e23a93389233ab2f80f63dee09488d9b"},
|
{file = "regex-2020.5.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ea55b80eb0d1c3f1d8d784264a6764f931e172480a2f1868f2536444c5f01e01"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:78014742b047ea2c9849e85db2dc84cad365b7f2dee404f86882d1fb9ff38df2"},
|
{file = "regex-2020.5.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c9bce6e006fbe771a02bda468ec40ffccbf954803b470a0345ad39c603402577"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:078024a52a72c98f157c380bea7beab7e0b907854ca337e3d4eecd44b363d4fb"},
|
{file = "regex-2020.5.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:d881c2e657c51d89f02ae4c21d9adbef76b8325fe4d5cf0e9ad62f850f3a98fd"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-win32.whl", hash = "sha256:4088da3b99f1341c2904679ffbfe6f08f5cc13e5cad1c6b053728da7cf68d28c"},
|
{file = "regex-2020.5.14-cp36-cp36m-win32.whl", hash = "sha256:99568f00f7bf820c620f01721485cad230f3fb28f57d8fbf4a7967ec2e446994"},
|
||||||
{file = "regex-2020.5.13-cp36-cp36m-win_amd64.whl", hash = "sha256:14026bf953137a5cdc6a2a8fda314a0296ce5cd6da6e46fe79d6e03caf3c1bac"},
|
{file = "regex-2020.5.14-cp36-cp36m-win_amd64.whl", hash = "sha256:70c14743320a68c5dac7fc5a0f685be63bc2024b062fe2aaccc4acc3d01b14a1"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:03284826c5f5cd4b252ab7d510cb4d6b7e7f849cdea8e1b384c57e0996a9a3f9"},
|
{file = "regex-2020.5.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a7c37f048ec3920783abab99f8f4036561a174f1314302ccfa4e9ad31cb00eb4"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36e6e7ca33a70f859e532110c2b1af28f98622424681e2e158f0f49735f1a9c8"},
|
{file = "regex-2020.5.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:89d76ce33d3266173f5be80bd4efcbd5196cafc34100fdab814f9b228dee0fa4"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:707d2fcfdce12ca946830839c4c243c1b258c0d2c61350dc2efe0af00a4d2fdf"},
|
{file = "regex-2020.5.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:51f17abbe973c7673a61863516bdc9c0ef467407a940f39501e786a07406699c"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:76668d1f91af08275313799bbd6ab04971ab08919defde886f5c94c7fc0f4623"},
|
{file = "regex-2020.5.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ce5cc53aa9fbbf6712e92c7cf268274eaff30f6bd12a0754e8133d85a8fb0f5f"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-win32.whl", hash = "sha256:0be1cb724d43a7fb017e868d809640de03be7db88ab83d212fd802bc3d0277a0"},
|
{file = "regex-2020.5.14-cp37-cp37m-win32.whl", hash = "sha256:8044d1c085d49673aadb3d7dc20ef5cb5b030c7a4fa253a593dda2eab3059929"},
|
||||||
{file = "regex-2020.5.13-cp37-cp37m-win_amd64.whl", hash = "sha256:9d42213927eede46bd4eb0c225600d82cf2b3c8882eb449f00b7cfa9bbf0975e"},
|
{file = "regex-2020.5.14-cp37-cp37m-win_amd64.whl", hash = "sha256:c2062c7d470751b648f1cacc3f54460aebfc261285f14bc6da49c6943bd48bdd"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7163cac9621e9099dc8768ddec32372f50893c934be961f58826d46f0668ea0c"},
|
{file = "regex-2020.5.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:329ba35d711e3428db6b45a53b1b13a0a8ba07cbbcf10bbed291a7da45f106c3"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:efae79f5eb3a6e2d9fa246605870887e0187ed66a4c56fc4cf22da00aa2ec220"},
|
{file = "regex-2020.5.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:579ea215c81d18da550b62ff97ee187b99f1b135fd894a13451e00986a080cad"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:89d4a97682170c32a13c4223e72cf75f9d0c6223b40fb4abe717bbb6c271c6ea"},
|
{file = "regex-2020.5.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:3a9394197664e35566242686d84dfd264c07b20f93514e2e09d3c2b3ffdf78fe"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4f144eb977800d7ef593893204c2e7f1f109e8a943f6e822990d22385b12c5d6"},
|
{file = "regex-2020.5.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ce367d21f33e23a84fb83a641b3834dd7dd8e9318ad8ff677fbfae5915a239f7"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-win32.whl", hash = "sha256:3b7f9c0051179325d7efd76271b783de593a50608e48e130c1590a95ea411f59"},
|
{file = "regex-2020.5.14-cp38-cp38-win32.whl", hash = "sha256:1386e75c9d1574f6aa2e4eb5355374c8e55f9aac97e224a8a5a6abded0f9c927"},
|
||||||
{file = "regex-2020.5.13-cp38-cp38-win_amd64.whl", hash = "sha256:3cec763e0428beb8668edf004aa9a8a4c1b502d0ecb97dc4d77e5a67a9077186"},
|
{file = "regex-2020.5.14-cp38-cp38-win_amd64.whl", hash = "sha256:7e61be8a2900897803c293247ef87366d5df86bf701083b6c43119c7c6c99108"},
|
||||||
{file = "regex-2020.5.13.tar.gz", hash = "sha256:cd931f9cca663617ba7a74b6500c6857405271b142edff68b2530e458b80a116"},
|
{file = "regex-2020.5.14.tar.gz", hash = "sha256:ce450ffbfec93821ab1fea94779a8440e10cf63819be6e176eb1973a6017aff5"},
|
||||||
]
|
]
|
||||||
six = [
|
six = [
|
||||||
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
|
{file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"},
|
||||||
|
@ -1,31 +1,19 @@
|
|||||||
aiomysql==0.0.20
|
#
|
||||||
aiosqlite==0.13.0
|
# This file is autogenerated by pip-compile
|
||||||
anyio==1.3.0
|
# To update, run:
|
||||||
appdirs==1.4.4
|
#
|
||||||
async-generator==1.10
|
# make up
|
||||||
asyncclick==7.0.9
|
#
|
||||||
asynctest==0.13.0
|
aiomysql==0.0.20 # via alice (setup.py)
|
||||||
attrs==19.3.0
|
aiosqlite==0.13.0 # via alice (setup.py)
|
||||||
black==19.10b0
|
anyio==1.3.0 # via alice (setup.py), asyncclick
|
||||||
cffi==1.14.0
|
async-generator==1.10 # via alice (setup.py), anyio
|
||||||
ciso8601==2.1.3; sys_platform != "win32" and implementation_name == "cpython"
|
asyncclick==7.0.9 # via alice (setup.py)
|
||||||
click==7.1.2
|
cffi==1.14.0 # via alice (setup.py), cryptography
|
||||||
cryptography==2.9.2
|
cryptography==2.9.2 # via alice (setup.py), pymysql
|
||||||
flake8==3.8.1
|
pycparser==2.20 # via alice (setup.py), cffi
|
||||||
iso8601==0.1.12; sys_platform == "win32" or implementation_name != "cpython"
|
pymysql==0.9.2 # via aiomysql, alice (setup.py)
|
||||||
isort==4.3.21
|
pypika==0.37.6 # via alice (setup.py)
|
||||||
mccabe==0.6.1
|
six==1.14.0 # via alice (setup.py), cryptography
|
||||||
pathspec==0.8.0
|
sniffio==1.1.0 # via alice (setup.py), anyio
|
||||||
pycodestyle==2.6.0
|
typing-extensions==3.7.4.2 # via alice (setup.py)
|
||||||
pycparser==2.20
|
|
||||||
pyflakes==2.2.0
|
|
||||||
pymysql==0.9.2
|
|
||||||
pypika==0.37.6
|
|
||||||
regex==2020.5.13
|
|
||||||
six==1.14.0
|
|
||||||
sniffio==1.1.0
|
|
||||||
taskipy==1.2.1
|
|
||||||
toml==0.10.1
|
|
||||||
-e git+https://github.com/tortoise/tortoise-orm.git@95c384a4742ee5980f8e4ae934bfdb0d8137bb40#egg=tortoise-orm
|
|
||||||
typed-ast==1.4.1
|
|
||||||
typing-extensions==3.7.4.2
|
|
||||||
|
18
setup.cfg
Normal file
18
setup.cfg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[flake8]
|
||||||
|
max-line-length = 100
|
||||||
|
exclude =
|
||||||
|
ignore = E501,W503,DAR101,DAR201,DAR402
|
||||||
|
|
||||||
|
[darglint]
|
||||||
|
docstring_style=sphinx
|
||||||
|
|
||||||
|
[isort]
|
||||||
|
not_skip=__init__.py
|
||||||
|
multi_line_output=3
|
||||||
|
include_trailing_comma=True
|
||||||
|
force_grid_wrap=0
|
||||||
|
use_parentheses=True
|
||||||
|
line_length=100
|
||||||
|
|
||||||
|
|
||||||
|
|
3
setup.py
3
setup.py
@ -37,7 +37,8 @@ setup(
|
|||||||
},
|
},
|
||||||
platforms='any',
|
platforms='any',
|
||||||
keywords=(
|
keywords=(
|
||||||
'migrations Tortoise-ORM mysql'
|
'migrate Tortoise-ORM mysql'
|
||||||
),
|
),
|
||||||
|
dependency_links=['https://github.com/tortoise/tortoise-orm.git@branch#egg=tortoise-orm'],
|
||||||
install_requires=requirements(),
|
install_requires=requirements(),
|
||||||
)
|
)
|
||||||
|
@ -29,8 +29,6 @@ class User(Model):
|
|||||||
is_superuser = fields.BooleanField(default=False, description="Is SuperUser")
|
is_superuser = fields.BooleanField(default=False, description="Is SuperUser")
|
||||||
avatar = fields.CharField(max_length=200, default="")
|
avatar = fields.CharField(max_length=200, default="")
|
||||||
intro = fields.TextField(default="")
|
intro = fields.TextField(default="")
|
||||||
created_at = fields.DatetimeField(auto_now_add=True)
|
|
||||||
updated_at = fields.DatetimeField(auto_now=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.pk}#{self.username}"
|
return f"{self.pk}#{self.username}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user