Skip to content

Commit

Permalink
complete InspectDb
Browse files Browse the repository at this point in the history
  • Loading branch information
long2ice committed Dec 25, 2020
1 parent 55a6d4b commit 5ae8b9e
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 9 deletions.
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

## Introduction

Aerich is a database migrations tool for Tortoise-ORM, which like alembic for SQLAlchemy, or Django ORM with it\'s
own migrations solution.
Aerich is a database migrations tool for Tortoise-ORM, which like alembic for SQLAlchemy, or Django ORM with it\'s own
migrations solution.

**Important: You can only use absolutely import in your `models.py` to make `aerich` work.**

Expand Down Expand Up @@ -40,14 +40,14 @@ Commands:
history List all migrate items.
init Init config file and generate root migrate location.
init-db Generate schema and generate app migrate location.
inspectdb Introspects the database tables to standard output as...
migrate Generate migrate changes file.
upgrade Upgrade to latest version.
```

## Usage

You need add `aerich.models` to your `Tortoise-ORM` config first,
example:
You need add `aerich.models` to your `Tortoise-ORM` config first, example:

```python
TORTOISE_ORM = {
Expand Down Expand Up @@ -109,7 +109,8 @@ Success migrate 1_202029051520102929_drop_column.sql
Format of migrate filename is
`{version_num}_{datetime}_{name|update}.sql`.

And if `aerich` guess you are renaming a column, it will ask `Rename {old_column} to {new_column} [True]`, you can choice `True` to rename column without column drop, or choice `False` to drop column then create.
And if `aerich` guess you are renaming a column, it will ask `Rename {old_column} to {new_column} [True]`, you can
choice `True` to rename column without column drop, or choice `False` to drop column then create.

### Upgrade to latest version

Expand Down Expand Up @@ -163,6 +164,33 @@ Now your db rollback to specified version.
1_202029051520102929_drop_column.sql
```

### Inspect db tables to TortoiseORM model

```shell
Usage: aerich inspectdb [OPTIONS]

Introspects the database tables to standard output as TortoiseORM model.

Options:
-t, --table TEXT Which tables to inspect.
-h, --help Show this message and exit.
```

Inspect all tables and print to console:

```shell
aerich --app models inspectdb -t user
```

Inspect a specified table in default app and redirect to `models.py`:

```shell
aerich inspectdb -t user > models.py
```

Note that this command is restricted, which is not supported in some solutions, such as `IntEnumField`
and `ForeignKeyField` and so on.

### Multiple databases

```python
Expand All @@ -173,7 +201,7 @@ tortoise_orm = {
},
"apps": {
"models": {"models": ["tests.models", "aerich.models"], "default_connection": "default"},
"models_second": {"models": ["tests.models_second"], "default_connection": "second",},
"models_second": {"models": ["tests.models_second"], "default_connection": "second", },
},
}
```
Expand Down
60 changes: 57 additions & 3 deletions aerich/inspectdb.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sys
from typing import List, Optional

from ddlparse import DdlParse
Expand All @@ -6,6 +7,17 @@


class InspectDb:
_table_template = "class {table}(Model):\n"
_field_template_mapping = {
"INT": " {field} = fields.IntField({pk}{unique}{comment})",
"SMALLINT": " {field} = fields.IntField({pk}{unique}{comment})",
"TINYINT": " {field} = fields.BooleanField({null}{default}{comment})",
"VARCHAR": " {field} = fields.CharField({pk}{unique}{length}{null}{default}{comment})",
"LONGTEXT": " {field} = fields.TextField({null}{default}{comment})",
"TEXT": " {field} = fields.TextField({null}{default}{comment})",
"DATETIME": " {field} = fields.DatetimeField({null}{default}{comment})",
}

def __init__(self, conn: BaseDBAsyncClient, tables: Optional[List[str]] = None):
self.conn = conn
self.tables = tables
Expand All @@ -14,9 +26,9 @@ def __init__(self, conn: BaseDBAsyncClient, tables: Optional[List[str]] = None):
async def show_create_tables(self):
if self.DIALECT == MySQLSchemaGenerator.DIALECT:
if not self.tables:
sql_tables = f"SELECT table_name FROM information_schema.tables WHERE table_schema = '{self.conn.database}';"
sql_tables = f"SELECT table_name FROM information_schema.tables WHERE table_schema = '{self.conn.database}';" # nosec: B608
ret = await self.conn.execute_query(sql_tables)
self.tables = map(lambda x: x[0], ret)
self.tables = map(lambda x: x["TABLE_NAME"], ret[1])
for table in self.tables:
sql_show_create_table = f"SHOW CREATE TABLE {table}"
ret = await self.conn.execute_query(sql_show_create_table)
Expand All @@ -26,7 +38,49 @@ async def show_create_tables(self):

async def inspect(self):
ddl_list = self.show_create_tables()
result = "from tortoise import Model, fields\n\n\n"
tables = []
async for ddl in ddl_list:
parser = DdlParse(ddl, DdlParse.DATABASE.mysql)
table = parser.parse()
print(table)
name = table.name.title()
columns = table.columns
fields = []
model = self._table_template.format(table=name)
for column_name, column in columns.items():
comment = default = length = unique = null = pk = ""
if column.primary_key:
pk = "pk=True, "
if column.unique:
unique = "unique=True, "
if column.data_type == "VARCHAR":
length = f"max_length={column.length}, "
if not column.not_null:
null = "null=True, "
if column.default is not None:
if column.data_type == "TINYINT":
default = f"default={'True' if column.default == '1' else 'False'}, "
elif column.data_type == "DATETIME":
if "CURRENT_TIMESTAMP" in column.default:
if "ON UPDATE CURRENT_TIMESTAMP" in ddl:
default = "auto_now_add=True, "
else:
default = "auto_now=True, "
else:
default = f"default={column.default}, "

if column.comment:
comment = f"description='{column.comment}', "

field = self._field_template_mapping[column.data_type].format(
field=column_name,
pk=pk,
unique=unique,
length=length,
null=null,
default=default,
comment=comment,
)
fields.append(field)
tables.append(model + "\n".join(fields))
sys.stdout.write(result + "\n\n\n".join(tables))

0 comments on commit 5ae8b9e

Please sign in to comment.