【发布时间】:2021-07-19 06:37:54
【问题描述】:
我有以下使用 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.group"], "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",
"models.group",
"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", exclude_readonly=True, 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_readonly=True, exclude=['members'])
UpdateGroup = pydantic_model_creator(Group, name="UpdateGroup", exclude_readonly=True, 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.
请注意,它会这样做几秒钟,然后应用程序会正确启动, 我还尝试将 pydantic 模型创建分离到一个名为“schema”的单独文件夹中,但这也没有做任何事情
【问题讨论】:
-
如何为会员筛选结果?例如,我想获取一个用户所属的所有组?如果我想要用户拥有的唯一组,将使用
filter(owner_id=user.id),但是如果一个组有很多成员,如何处理成员? -
@DiegoGaona 我通常更喜欢在模型中定义关系和逆向,乌龟会自动加载它,您可以定义 pydantic 模型以包含您需要或想要省略的所有内容,这样当您取回用户对象,您有一个包含所有加入组的数组。相同的概念,但与组相反,您可以在组模型中定义关系并与所有加入的用户一起返回一个数组,这是一个示例响应 codebeautify.org/online-json-editor/cb8e288b
-
我会在几天内尝试做类似的事情(现在时间很短)。我发布了一个问题(与之相关),如果你能帮忙的话:stackoverflow.com/questions/67971593/…。不过还是谢谢!!
标签: python fastapi tortoise-orm