问题描述
我使用 fastapi 实现了以下实现。
我目前的问题是,我终生无法在乌龟模型上执行 early init
以将关系恢复到模式中。
我试过基本上到处都转储以下行,但它似乎不起作用。
Tortoise.init_models(["models.user","models.group"],"models")
我也试过以这种方式使用 prefetch_related
但这也不起作用
GetGroup.from_queryset(Group.get(id=3).prefetch_related('owner'))
我已经在谷歌上搜索了几个小时,但没有找到一个具体的答案/方法来让它正常工作。
文件夹结构:
app
│ main.py
└───database
│ │ database.py
└───models
| │ user.py
| │ group.py
└───routers
| │ user_router.py
| │ group_router.py
└───services
| │ auth.py
主文件
from fastapi import FastAPI
from database.database import init_db
from routers.user_router import router as UserRouter
from routers.group_router import router as GroupRouter
# Instantiate the Application
app = FastAPI(title="test",root_path="/api/")
# Include the Routers
app.include_router(UserRouter,tags=["User"],prefix="/user")
app.include_router(GroupRouter,tags=["Group"],prefix="/group")
# Start DB Connection on Startup
@app.on_event("startup")
async def startup_event():
init_db(app)
数据库/数据库.py
from decouple import config
from fastapi import FastAPI
from tortoise import Tortoise
from tortoise.contrib.fastapi import register_tortoise
def init_db(app: FastAPI) -> None:
Tortoise.init_models(["models.user","models")
register_tortoise(
app,db_url=f"MysqL://{config('MysqL_USER')}:{config('MysqL_PASSWORD')}@{config('MysqL_HOST')}:{config('MysqL_EXPOSE')}/{config('MysqL_DB')}",modules={"models": ["models.user","models.group",]},generate_schemas=False,add_exception_handlers=True,)
TORTOISE_ORM = {
"connections": {"default": f"MysqL://{config('MysqL_USER')}:{config('MysqL_PASSWORD')}@{config('MysqL_HOST')}:{config('MysqL_EXPOSE')}/{config('MysqL_DB')}"},"apps": {
"models": {
"models": ["models.user","aerich.models"],"default_connection": "default",},}
模型/用户.py
from tortoise import fields
from tortoise.models import Model
from tortoise.contrib.pydantic import pydantic_model_creator
from models.group import Group
class User(Model):
# ##### Define Readonly Fields ##### #
id = fields.BigIntField(pk=True)
# ##### Define normal Fields ##### #
first_name = fields.CharField(max_length=50)
last_name = fields.CharField(max_length=50)
username = fields.CharField(max_length=50,unique=True)
email = fields.CharField(max_length=50,unique=True)
password = fields.CharField(max_length=128)
# ##### Define O2M ##### #
owned_groups: fields.ReverseRelation[Group]
# ##### Define M2M ##### #
groups: fields.ManyToManyRelation[Group]
# ##### Define Time_Stamps ###### #
created_at = fields.DatetimeField(auto_Now_add=True)
modified_at = fields.DatetimeField(auto_Now=True)
class Meta:
table: str = 'users'
AuthData = pydantic_model_creator(User)
createuser = pydantic_model_creator(User,name="createuser",exclude_readonly=True)
UpdateUser = pydantic_model_creator(User,name="UpdateUser",exclude_readonly=True,exclude=['password'])
GetUser = pydantic_model_creator(User,name="GetUser",exclude=['password'])
ChangeUserPassword = pydantic_model_creator(User,name="ChangeUserPassword",include=['password'])
模型/组.py
from tortoise import fields
from tortoise.models import Model
from tortoise.contrib.pydantic import pydantic_model_creator
class Group(Model):
# ##### Define Readonly Fields ##### #
id = fields.BigIntField(pk=True)
# ##### Define normal Fields ##### #
name = fields.CharField(max_length=50,unique=True)
# ##### Define O2M ##### #
owner = fields.ForeignKeyField("models.User",related_name="owned_groups")
# ##### Define M2M ##### #
members = fields.ManyToManyField("models.User",related_name="groups")
# ##### Define Time_Stamps ###### #
created_at = fields.DatetimeField(auto_Now_add=True)
modified_at = fields.DatetimeField(auto_Now=True)
class Meta:
table: str = 'groups'
CreateGroup = pydantic_model_creator(Group,name="CreateGroup",exclude=['members'])
UpdateGroup = pydantic_model_creator(Group,name="UpdateGroup",exclude=['members'])
GetGroup = pydantic_model_creator(Group,name="GetGroup")
路由器/group_router.py
from typing import List
import json
from fastapi import HTTPException,APIRouter,Depends,status
from models.user import User
from models.group import Group,CreateGroup,UpdateGroup,GetGroup
from tortoise.contrib.fastapi import HTTPNotFoundError
from services.auth import current_user
# Intialize Router
router = APIRouter()
# ###################### Define Routes ###################### #
# Create A Group
@router.post("/",dependencies=[Depends(current_user)])
async def create_group(group: CreateGroup,user: User = Depends(current_user)):
user = await user
try:
await Group.create(**group.dict(exclude_unset=True),owner_id=user.id)
except Exception:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,detail="Group Name Already Exists"
)
return {"Group Created Successfully"}
# Update A Group
@router.put("/{group_id}",dependencies=[Depends(current_user)],responses={404: {"model": HTTPNotFoundError}})
async def update_group(group_id: int,group: UpdateGroup,user: User = Depends(current_user)):
user = await user
group = await GetGroup.from_queryset_single(Group.get(id=group_id))
return group
if user.id == group.id:
await Group.filter(id=group_id).update(**group.dict(exclude_unset=True))
return {"Group Successfully Updated"}
else:
raise HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE,detail="You can't edit a group unless you're the owner"
)
# Get All Groups
@router.get("/",dependencies=[Depends(current_user)])
async def get_groups():
return GetGroup.schema()
如您所见,最后一行 GetGroup.schema()
永远不会返回关系。
我尝试在容器启动时捕获日志并得到以下结果
[2021-04-24 19:59:53 +0000] [1255] [INFO] Started server process [1255]
[2021-04-24 19:59:53 +0000] [1255] [INFO] Waiting for application startup.
[2021-04-24 19:59:53 +0000] [1255] [ERROR] Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/tortoise/__init__.py",line 358,in _discover_models
module = importlib.import_module(models_path)
File "/usr/local/lib/python3.8/importlib/__init__.py",line 127,in import_module
return _bootstrap._gcd_import(name[level:],package,level)
File "<frozen importlib._bootstrap>",line 1014,in _gcd_import
File "<frozen importlib._bootstrap>",line 991,in _find_and_load
File "<frozen importlib._bootstrap>",line 973,in _find_and_load_unlocked
ModuleNotFoundError: No module named 'a'
During handling of the above exception,another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/starlette/routing.py",line 526,in lifespan
async for item in self.lifespan_context(app):
File "/usr/local/lib/python3.8/site-packages/starlette/routing.py",line 467,in default_lifespan
await self.startup()
File "/usr/local/lib/python3.8/site-packages/starlette/routing.py",line 502,in startup
await handler()
File "/app/main.py",line 17,in startup_event
init_db(app)
File "/app/database/database.py",line 8,in init_db
Tortoise.init_models("app.models.user","models")
File "/usr/local/lib/python3.8/site-packages/tortoise/__init__.py",line 415,in init_models
app_models += cls._discover_models(models_path,app_label)
File "/usr/local/lib/python3.8/site-packages/tortoise/__init__.py",line 360,in _discover_models
raise ConfigurationError(f'Module "{models_path}" not found')
tortoise.exceptions.ConfigurationError: Module "a" not found
[2021-04-24 19:59:53 +0000] [1255] [ERROR] Application startup Failed. Exiting.
请注意,它会这样做几秒钟,然后应用程序正确启动, 我还尝试在名为“schema”的单独文件夹中分离 pydantic 模型创建,但也没有做任何事情
解决方法
所以我终于找到了答案,我会把它留在这里,以防有些可怜的人偶然发现这个问题
诀窍是移动
from database.database import init_db
到 main.py
文件的顶部
并添加
Tortoise.init_models(["models.user","models.group"],"models")
在 init_db() 函数下方,以便在 register_tortoise
函数执行和所有模型初始化之前调用它