如何解决Tortoise-ORM + FastAPI 中未显示模型关系
我正在使用 Tortoise-ORM 作为它的 orm 来玩 FastAPI 并遇到了问题。具体来说,我无法在模型中返回关系。
这是我的应用程序结构。 Structure 的灵感来自 Django 的应用程序结构。
.
├── Dockerfile
├── LICENSE
├── Pipfile
├── Pipfile.lock
├── README.md
├── app
│ ├── contacts
│ │ ├── __init__.py
│ │ ├── main.py
│ │ ├── models.py
│ │ └── routers.py
│ ├── main.py
│ ├── resources
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ ├── core_model.py
│ │ ├── database.py
│ │ └── middlewares.py
│ └── users
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ └── routers.py
└── docker-compose.yml
数据库连接在 app/resources/database.py
中设置如下;
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
def get_db_uri(*,user,password,host,db):
return f'postgres://{user}:{password}@{host}:5432/{db}'
def setup_database(app: FastAPI):
register_tortoise(
app,db_url=get_db_uri(
user='postgres',password='postgres',host='db',# docker-compose service name
db='postgres',),modules={
'models': [
'app.users.models','app.contacts.models',],},generate_schemas=True,add_exception_handlers=True,)
从 models
参数中您可以看到有 2 个模型设置。这是两个。
app/users/models.py
from tortoise import Tortoise,fields
from tortoise.contrib.pydantic import pydantic_model_creator
from passlib.hash import bcrypt
from app.resources.core_model import CoreModel
class User(CoreModel):
username = fields.CharField(50,unique=True)
email = fields.CharField(60,unique=True)
password_hash = fields.CharField(128)
def __str__(self):
return self.email
def verify_password(self,password):
return bcrypt.verify(password,self.password_hash)
class PydanticMeta:
exclude = ["password_hash"]
# Tortoise.init_models(['app.users.models'],'models')
User_Pydantic = pydantic_model_creator(User,name='User')
UserIn_Pydantic = pydantic_model_creator(
User,name='UserIn',exclude_readonly=True)
app/contacts/models.py
from tortoise import Tortoise,fields
from tortoise.contrib.pydantic import pydantic_model_creator
from app.resources.core_model import CoreModel
class Contact(CoreModel):
user = fields.ForeignKeyField(
'models.User',related_name='contacts')
name = fields.CharField(50)
def __str__(self):
return self.name
# Tortoise.init_models(['app.users'],'models')
Contact_Pydantic = pydantic_model_creator(Contact,name='Contact')
ContactIn_Pydantic = pydantic_model_creator(
Contact,name='ContactIn',exclude_readonly=True)
以下是用户尝试保存联系人时发生的情况。
@router.post('/',status_code=status.HTTP_201_CREATED)
async def create_contact(contact_name: str = Form(...),user: User_Pydantic = Depends(get_current_user)):
try:
contact = Contact(user_id=user.id,name=contact_name)
await contact.save()
contact_obj = await Contact_Pydantic.from_tortoise_orm(contact)
print(contact_obj.schema_json(indent=4))
except Exception as err:
print(err)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,detail='Failed to save data')
return {'status_code': status.HTTP_201_CREATED,'contact': contact_obj.dict()}
用户可以从以下路线检索他们保存的联系人。
@router.get('/me')
async def get_all_contacts(user: User_Pydantic = Depends(get_current_user)):
try:
contacts = await Contact.filter(user_id=user.id)
contacts_list = [await Contact_Pydantic.from_tortoise_orm(contact) for contact in contacts]
print(user.schema_json())
except:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,detail='Failed to fetch related data')
return {'contacts': contacts_list}
当用户检索他们的联系人时,它不会显示与用户的关系。在这个例子中,它不是必需的,但我想弄清楚如何在响应中获取关系以供将来参考。
我浏览了文档,发现 early-init 是一个东西,并尝试使用 init_models
但这似乎不起作用。或者也许我只是不知道它是如何工作的。如果我需要使用 init_models
,令人困惑的部分是 1. 何时调用它和 2. 如何调用它。 init_models
两个论点让我很困惑。
总结一下,我有两个问题。
提前致谢。
注意
- 我尝试了以下方法来保存联系人,但结果是一样的。
@router.post('/',user: User_Pydantic = Depends(get_current_user)):
try:
user = await User.get(id=user.id)
contact = await Contact.create(user=user,name=contact_name)
contact_obj = await Contact_Pydantic.from_tortoise_orm(contact)
print(contact_obj.schema_json(indent=4))
except Exception as err:
print(err)
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,'contact': contact_obj.dict()}
解决方法
感谢@alex_noname,我可以做到这一点。
首先,将 pydantic 模式与 models.py 分开。
.
├── Dockerfile
├── LICENSE
├── Pipfile
├── Pipfile.lock
├── README.md
├── app
│ ├── contacts
│ │ ├── __init__.py
│ │ ├── main.py
│ │ ├── models.py
│ │ ├── routers.py
│ │ └── schemas.py <- new
│ ├── main.py
│ ├── resources
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ ├── core_model.py
│ │ ├── database.py
│ │ └── middlewares.py
│ └── users
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ ├── routers.py
│ └── schemas.py <- new
└── docker-compose.yml
app/users/schemas.py
from tortoise.contrib.pydantic import pydantic_model_creator
from .models import User
User_Pydantic = pydantic_model_creator(User,name='User')
UserIn_Pydantic = pydantic_model_creator(
User,name='UserIn',exclude_readonly=True)
app/contacts/schemas.py
from tortoise.contrib.pydantic import pydantic_model_creator
from .models import Contact
Contact_Pydantic = pydantic_model_creator(Contact,name='Contact')
ContactIn_Pydantic = pydantic_model_creator(
Contact,name='ContactIn',exclude_readonly=True)
然后,在我们设置 tortoise-orm 的 app/resources/database.py 中,调用 init_models
。
from fastapi import FastAPI
from tortoise import Tortoise
from tortoise.contrib.fastapi import register_tortoise
def get_db_uri(*,user,password,host,db):
return f'postgres://{user}:{password}@{host}:5432/{db}'
def setup_database(app: FastAPI):
register_tortoise(
app,db_url=get_db_uri(
user='postgres',password='postgres',host='db',# docker-composeのservice名
db='postgres',),modules={
'models': [
'app.users.models','app.contacts.models',],},# modules={"models": ["_models_"]},generate_schemas=True,add_exception_handlers=True,)
Tortoise.init_models(['app.users.models','app.contacts.models'],'models') # <- added
就是这样!
这就像一个魅力。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。