Python 如何使pydantic在异步属性(tortoise orm';的反向外键)上等待?
(问题底部的MRE) 在乌龟orm中,我们必须等待反向外键字段:Python 如何使pydantic在异步属性(tortoise orm';的反向外键)上等待?,python,fastapi,pydantic,tortoise-orm,Python,Fastapi,Pydantic,Tortoise Orm,(问题底部的MRE) 在乌龟orm中,我们必须等待反向外键字段: comments=wait Post.get(id=id).comments 但在fastapi中,当返回Post实例时,pydantic抱怨: pydantic.error_wrappers.ValidationError: 1 validation error for PPost response -> comments value is not a valid list (type=type_error.list
comments=wait Post.get(id=id).comments
但在fastapi中,当返回Post实例时,pydantic抱怨:
pydantic.error_wrappers.ValidationError: 1 validation error for PPost
response -> comments
value is not a valid list (type=type_error.list)
这很有意义,因为comments
属性返回协同路由。我不得不用这个小技巧来解决这个问题:
post=post.get(id=id)
返回{**post.\uuuu dict\uuuuuu,'comments':wait post.comments}
然而,真正的问题是当我有多个关系时:返回一个用户的帖子和评论。在这种情况下,我不得不以一种非常丑陋的方式(听起来不太好)将我的实体模型转换成dict
下面是要复制的代码(尽量保持简单):
型号.py
从tortoise.fields导入*
从乌龟。模型导入模型
从tortoise导入tortoise,运行异步
异步def init_tortoise():
等待乌龟(
db_url=sqlite://db.sqlite3',
模块={'models':['models']},
)
等待乌龟。生成_模式()
类别用户(型号):
name=CharField(80)
班级职务(模范):
title=CharField(80)
content=TextField()
owner=ForeignKeyField('models.User',related_name='posts')
类别后通知(型号):
text=CharField(80)
post=ForeignKeyField('models.post',related_name='comments')
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
运行异步(init_tortoise())
__全部[
“用户”,
"岗位",,
"后承诺",,
“一只乌龟”,
]
main.py
导入异步IO
从输入导入列表开始
从fastapi导入fastapi,HTTPException
从pydantic导入BaseModel
从模型导入*
app=FastAPI()
asyncio.create_任务(init_tortoise())
#pydantic模型的前缀是P
类别建议(基本模型):
文本:str
类PPOT(基本模型):
id:int
标题:str
内容:str
注释:列表[PPOTCOMMENT]
类配置:
orm_模式=真
类PUser(基本模型):
id:int
姓名:str
帖子:列表[PPost]
类配置:
orm_模式=真
@app.get('/posts/{id}',response_model=PPost)
异步定义索引(id:int):
post=等待post。获取\或\无(id=id)
返回{**post.\uuuu dict\uuuuuu,'comments':wait post.comments}
@app.get('/users/{id}',response\u model=PUser)
异步定义索引(id:int):
用户=等待用户。获取或无(id=id)
返回{**user.\u dict\u'posts':wait user.posts}
/users/1
出现以下错误:
pydantic.error_wrappers.ValidationError: 1 validation error for PUser
response -> posts -> 0 -> comments
value is not a valid list (type=type_error.list)
此外,您可能希望将其放入init.py并运行:
导入异步IO
从模型导入*
异步def main():
等待初始化乌龟()
u=wait User.create(name='drdilyor')
p=wait Post.create(title='foo',content='lorem ipsum',owner=u)
c=等待后通知。创建(text='spam egg',post=p)
asyncio.run(main())
我想要的是让pydantic自动等待这些异步字段(这样我就可以返回Post实例)。用pydantic怎么可能
更改
/posts/{id}
以返回post及其所有者而不添加注释,在使用这种方式时实际起作用(感谢@papple23j):
return wait Post.get\u或\u none(id=id)。预取\u相关('owner'))
但不适用于反向外键。另外,
select_related('comments')
没有帮助,它正在引发AttributeError:can't set attribute
您可以尝试使用prefetch_related()
例如:
@app.get('/posts/{id}',response_model=PPost)
异步定义索引(id:int):
post=等待post.获取\或\无(id=id).预取\相关('注释')
返回{**post.\u dict\u}
(以下文本使用DeepL翻译)
有一种方法可以做到这一点,但它有点棘手
首先将pydantic模型片段拆分为schemas.py
来自pydantic import BaseModel的
从输入导入列表开始
#pydantic模型的前缀是P
类别建议(基本模型):
文本:str
类配置:
orm_mode=True#添加此行
类PPOT(基本模型):
id:int
标题:str
内容:str
注释:列表[PPOTCOMMENT]
类配置:
orm_模式=真
类PUser(基本模型):
id:int
姓名:str
帖子:列表[PPost]
类配置:
orm_模式=真
接下来,重写models.py
从tortoise.fields导入*
从乌龟。模型导入模型
从tortoise导入tortoise,运行异步
从模式导入*
异步def init_tortoise():
等待乌龟(
db_url=sqlite://db.sqlite3',
模块={'models':['models']},
)
等待乌龟。生成_模式()
类别用户(型号):
name=CharField(80)
_posts=反向关联[“Post”]#1
@财产
def职位(自我):#3
返回[post.from_orm(post)for post in self.\u posts]
班级职务(模范):
title=CharField(80)
content=TextField()
owner=ForeignKeyField('models.User',related_name='u posts')2
_注释=反向关联[“后注释”]#1
@财产
def评论(自我):#3
返回[posttcomment.from_orm(comment)for comment in self.\u comments]
类别后通知(型号):
text=CharField(80)
post=ForeignKeyField('models.post',相关名称='u注释')2
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
运行异步(init_tortoise())
__全部[
“用户”,
"岗位",,
"后承诺",,
“一只乌龟”,
]
在哪里
#1:使用ReverseRelation
声明反向字段,这里使用底线的前缀来区分
#2:修改相关的\u名称
#3:编写一个属性函数并返回相应的pydantic模型列表,在这里您不需要使用await
,因为默认情况下是使用prefetch\u related()访问它。
PPost = pydantic_model_creator(Post)
# used as
return await PPost.from_tortoise_orm(await Post.get_or_none(id=1))
asyncio.create_task(init_tortoise())
asyncio.get_event_loop().run_until_complete(init_tortoise())
events = await tournament.events.all()
await tournament.fetch_related('events')
class PPost(BaseModel):
comments: List[PPostComment]
@validator('comments', pre=True)
def _iter_to_list(cls, v):
return list(v)