sha*_*uga 9 python pydantic fastapi
我正在尝试从 html 表单提交数据并使用 pydantic 模型对其进行验证。
使用此代码
from fastapi import FastAPI, Form
from pydantic import BaseModel
from starlette.responses import HTMLResponse
app = FastAPI()
@app.get("/form", response_class=HTMLResponse)
def form_get():
return '''<form method="post">
<input type="text" name="no" value="1"/>
<input type="text" name="nm" value="abcd"/>
<input type="submit"/>
</form>'''
class SimpleModel(BaseModel):
no: int
nm: str = ""
@app.post("/form", response_model=SimpleModel)
def form_post(form_data: SimpleModel = Form(...)):
return form_data
Run Code Online (Sandbox Code Playgroud)
我如何收到 http 状态422不可处理实体的错误
{"detail":[{"loc":["body","form_data"],"msg":"field required","type":"value_error.missing"}]}
等效的 curl 命令(由 firfox 生成)是
curl ' http://localhost:8001/form ' -H 'Content-Type: application/x-www-form-urlencoded' --data 'no=1&nm=abcd'
这里的请求正文包含 no=1&nm=abcd
我究竟做错了什么?
Nik*_*dov 21
我找到了一个解决方案,它可以帮助我们将 FastAPI 表单也用作 pydantic :)
我的代码:
class AnyForm(BaseModel):
any_param: str
any_other_param: int = 1
@classmethod
def as_form(
cls,
any_param: str = Form(...),
any_other_param: int = Form(1)
) -> AnyForm:
return cls(any_param=any_param, any_other_param=any_other_param)
@router.post('')
async def any_view(form_data: AnyForm = Depends(AnyForm.as_form)):
...
Run Code Online (Sandbox Code Playgroud)
我认为它像往常一样以招摇的形式显示,它可以写得更通用,也许我会返回并编辑答案。
[更新]
我把它写成更通用的装饰器
import inspect
from typing import Type
from fastapi import Form
from pydantic import BaseModel
from pydantic.fields import ModelField
def as_form(cls: Type[BaseModel]):
new_parameters = []
for field_name, model_field in cls.__fields__.items():
model_field: ModelField # type: ignore
if not model_field.required:
new_parameters.append(
inspect.Parameter(
model_field.alias,
inspect.Parameter.POSITIONAL_ONLY,
default=Form(model_field.default),
annotation=model_field.outer_type_,
)
)
else:
new_parameters.append(
inspect.Parameter(
model_field.alias,
inspect.Parameter.POSITIONAL_ONLY,
default=Form(...),
annotation=model_field.outer_type_,
)
)
async def as_form_func(**data):
return cls(**data)
sig = inspect.signature(as_form_func)
sig = sig.replace(parameters=new_parameters)
as_form_func.__signature__ = sig # type: ignore
setattr(cls, 'as_form', as_form_func)
return cls
Run Code Online (Sandbox Code Playgroud)
用法看起来像
class Test1(BaseModel):
a: str
b: int
@as_form
class Test(BaseModel):
param: str
test: List[Test1]
test1: Test1
b: int = 1
a: str = '2342'
@router.post('/me', response_model=Test)
async def me(request: Request, form: Test = Depends(Test.as_form)):
return form
Run Code Online (Sandbox Code Playgroud)
iud*_*een 15
您可以使用数据类更简单地做到这一点
from dataclasses import dataclass
from fastapi import FastAPI, Form, Depends
from starlette.responses import HTMLResponse
app = FastAPI()
@app.get("/form", response_class=HTMLResponse)
def form_get():
return '''<form method="post">
<input type="text" name="no" value="1"/>
<input type="text" name="nm" value="abcd"/>
<input type="submit"/>
</form>'''
@dataclass
class SimpleModel:
no: int = Form(...)
nm: str = Form(...)
@app.post("/form")
def form_post(form_data: SimpleModel = Depends()):
return form_data
Run Code Online (Sandbox Code Playgroud)
小智 9
我实现了此处找到的解决方案Mause 解决方案,它似乎有效
from fastapi.testclient import TestClient
from fastapi import FastAPI, Depends, Form
from pydantic import BaseModel
app = FastAPI()
def form_body(cls):
cls.__signature__ = cls.__signature__.replace(
parameters=[
arg.replace(default=Form(...))
for arg in cls.__signature__.parameters.values()
]
)
return cls
@form_body
class Item(BaseModel):
name: str
another: str
@app.post('/test', response_model=Item)
def endpoint(item: Item = Depends(Item)):
return item
tc = TestClient(app)
r = tc.post('/test', data={'name': 'name', 'another': 'another'})
assert r.status_code == 200
assert r.json() == {'name': 'name', 'another': 'another'}
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以使用如下数据形式:
@app.post("/form", response_model=SimpleModel)
def form_post(no: int = Form(...),nm: str = Form(...)):
return SimpleModel(no=no,nm=nm)
Run Code Online (Sandbox Code Playgroud)