tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.55k stars 374 forks source link

aerich init-db raised error: ConfigurationError #1385

Open boziyoung opened 1 year ago

boziyoung commented 1 year ago

Describe the bug when i changed my models.py , added some new model classes , and then i excuted aerich migrate ,raiseed a "ConfigurationError" ; i decided delete all files under the migrations path , tried to excute "aerich init-db" raised same error : "ConfigurationError"

detailed error: Traceback (most recent call last): File "/home/ubuntu/english/env/lib/python3.11/site-packages/tortoise/init.py", line 119, in get_related_model return cls.apps[related_app_name][related_model_name]


KeyError: 'base'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ubuntu/english/env/bin/aerich", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/aerich/cli.py", line 258, in main
    cli()
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/click/decorators.py", line 26, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/aerich/cli.py", line 31, in wrapper
    loop.run_until_complete(f(*args, **kwargs))
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/aerich/cli.py", line 232, in init_db
    await command.init_db(safe)
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/aerich/__init__.py", line 129, in init_db
    await Tortoise.init(config=self.tortoise_config)
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/tortoise/__init__.py", line 575, in init
    cls._init_apps(apps_config)
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/tortoise/__init__.py", line 415, in _init_apps
    cls._init_relations()
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/tortoise/__init__.py", line 304, in _init_relations
    related_model = get_related_model(related_app_name, related_model_name)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ubuntu/english/env/lib/python3.11/site-packages/tortoise/__init__.py", line 122, in get_related_model
    raise ConfigurationError(f"No app with name '{related_app_name}' registered.")
tortoise.exceptions.ConfigurationError: No app with name 'base' registered.

models/base.py:
`
# from tortoise import Model, fields

from tortoise.models import Model
from tortoise import fields

class EnglishSentence(Model): 
    id = fields.IntField(pk=True)
    # mysql 一般char长度为 255 null=false 默认不可为空
    original = fields.CharField(max_length=255, description="英语原文", unique=True)
    translation = fields.CharField(max_length=255, description="中文翻译")
    notes = fields.TextField(description="笔记", null=True)
    create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
    update_time = fields.DatetimeField(auto_now=True, description="更新时间")

    class Meta:
        """
            对该数据库模型类进行相关 配置设置
        """
        table_description = "英语笔记"
        table = "notes"

    def __str__(self) -> str:
        return self.original

"""
new add model:
follwing code raise ConfigurationError
"""
class TimestampMixin(Model):
    create_time = fields.DatetimeField(auto_now_add=True, description='创建时间')
    update_time = fields.DatetimeField(auto_now=True, description="更新时间")

    class Meta:
        table = None   

class Role(TimestampMixin):
    role_name = fields.CharField(max_length=15, description="角色名称")

    user : fields.ManyToManyRelation["User"] = fields.ManyToManyField("base.User", related_name="role", on_delete=fields.CASCADE)
    access : fields.ManyToManyRelation["Access"] = fields.ManyToManyField("base.Access", related_name="role", on_delete=fields.CASCADE)

    role_status = fields.BooleanField(default=False, description="True: 启用 False: 禁用")
    role_desc = fields.CharField(null=True, max_length=255, description="角色描述")

    class Meta:
        table_description = "角色表"
        table = "role"

class User(TimestampMixin):
    role : fields.ManyToManyRelation[Role]

    user_name = fields.CharField(null=True, max_length=20, description="用户名", unique=True)
    user_type = fields.BooleanField(default=False, description="用户类型 True:超级管理员 False:普通管理员")
    password = fields.CharField(null=True, max_length=255)
    nickname = fields.CharField(default="Boziyoung", max_length=255, description='昵称')
    user_phone = fields.CharField(null=True, max_length=11 ,description="手机号", unique=True)
    user_email = fields.CharField(max_length=255, description="邮箱", unique=True)
    full_name = fields.CharField(max_length=255, null=True, description="姓名")
    user_status = fields.IntField(default=0, description='0未激活 1正常 2禁用')
    header_img = fields.CharField(null=True, max_length=255, description='头像')
    sex = fields.IntField(default=0, null=True, description='0未知 1男 2女')
    client_host = fields.CharField(null=True, max_length=19, description="访问IP")

    class Meta:
        table_description = "用户表"
        table = "user"

class Access(TimestampMixin):
    role: fields.ManyToManyRelation[Role]

    access_name = fields.CharField(max_length=15, description="权限名称")
    parent_id = fields.IntField(default=0, description='父id')
    scopes = fields.CharField(unique=True,max_length=255, description="权限范围标识")
    access_desc = fields.CharField(null=True, max_length=255, description="权限描述")
    menu_icon = fields.CharField(null=True, max_length=255, description='菜单图标')
    is_check = fields.BooleanField(default=False, description='是否验证权限 True为验证 False不验证')
    is_menu = fields.BooleanField(default=False, description='是否为菜单 True菜单 False不是菜单')

    class Meta:
        table_description = "权限表"
        table = "access"

class AccessLog(TimestampMixin):
    user_id = fields.IntField(description="用户ID")
    target_uel = fields.CharField(null=True, max_length=255, description="访问的url")
    user_agent = fields.CharField(null=True, max_length=255, description="访问的UA")
    request_params = fields.JSONField(null=True, description='请求参数get|post')
    ip = fields.CharField(null=True, max_length=32, description='访问ip')
    note = fields.CharField(null=True, max_length=255, description='备注') 

    class Meta:
        table_description = "用户操作记录表"
        table = "access_log"

`
database/mysql.py:
`from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
import os
"""
数据库配置
"""
DB_ORM_CONFIG = {
    # connections 可连接多数据库
    "connections" :{
    # 连接数据库 格式
        "english_notes":{
            'engine': 'tortoise.backends.mysql',
            "credentials": {
                'host': os.getenv('BASE_HOST', '127.0.0.1'),
                'user': os.getenv('BASE_USER', 'notes'),
                'password': os.getenv('BASE_PASSWORD', '*******.'),
                'port': int(os.getenv('BASE_PORT', 3306)),
                'database': os.getenv('BASE_DB', 'english_notes'),
            }
        }
    },

    "apps":{
        # 模型文件,数控库 ,实例化链接
        # aerich 是 tortoise orm 自己的数据库 迁移工具
        "english_notes":{"models": ["app.models.base", "aerich.models"], "default_connection": "english_notes"}
    },
   'use_tz': False,
    'timezone': 'Asia/Shanghai'
}

async def register_mysql(app: FastAPI):
    """
    注册数据库
    """
    register_tortoise(
        app,
        config=DB_ORM_CONFIG,
        # generate_schemas :如果数据库为空的情况下,会自动生成对应表单,生产环境不要开 (False); 但是不先把数据库创建好,不能写入数据信息    
        # 所以设置  generate_schemas: False时, 需要使用 aerich 来处理各种迁移数据库等问题
        generate_schemas=False,
        add_exception_handlers=True,  # true时: 详细的调试信息 
    )`

Expected result:
how can i migrate the model correctly
waketzheng commented 1 year ago

Try "app.models.base" --> "app.models"

"apps":{
    # 模型文件,数控库 ,实例化链接
    # aerich 是 tortoise orm 自己的数据库 迁移工具
    "english_notes":{"models": ["app.models", "aerich.models"], "default_connection": "english_notes"}
},