fastApi笔记09-响应模型

时间:2024-02-23 13:47:51

响应模型

可以在任意路径操作(get,post等)中使用response_model来声明用于返回响应模型

from typing import Any

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()


class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str | None = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
    return user

需要注意response_model是[装饰器]方法(get,post等)的一个参数,不像之前参数和请求,它不属于路径操作函数

请求体使用UserIn,响应模型使用UserOut,未在输出模型声明的参数都会被过滤掉,示例响应模型会把请求体中的密码给过滤掉。

响应模型编码参数

from typing import List, Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: float = 10.5
    tags: List[str] = []


items = {
    "foo": {"name": "Foo", "price": 50.2},
    "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
    "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}


@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]

@app.get(
    "/items/{item_id}/name",
    response_model=Item,
    response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
    return items[item_id]


@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
    return items[item_id]

在item_id都使用foo情况下

response_model_exclude_unset=True:响应会过滤默认值,只返回实际设置的值

{
  "name": "Foo",
  "price": 50.2
}

response_model_include:需要传一个set(),只显示set中设置的值

{
  "name": "Foo",
  "description": null
}

response_model_exclude:需要传一个set(),过滤set中设置的值

{
  "name": "Foo",
  "description": null,
  "price": 50.2,
  "tags": []
}

通过继承减少重复

对用户模型来说尤其如此,因为:

  • 输入模型需要拥有密码属性。
  • 输出模型不应该包含密码。
  • 数据库模型很可能需要保存密码的哈希值。
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()


class UserBase(BaseModel):
    username: str
    email: EmailStr
    full_name: str | None = None


class UserIn(UserBase):
    password: str


class UserOut(UserBase):
    pass


class UserInDB(UserBase):
    hashed_password: str


def fake_password_hasher(raw_password: str):
    return "supersecret" + raw_password


def fake_save_user(user_in: UserIn):
    hashed_password = fake_password_hasher(user_in.password)
    user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
    print("User saved! ..not really")
    return user_in_db


@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
    user_saved = fake_save_user(user_in)
    return user_saved

 可以先声明userBase为基础模型,然后创建继承该模型属性(类型声明,校验等)的子类,只要在子类中添加于基础模型不同的字段即可。

多个响应模型

可以将一个响应声明为两种类型的 Union,这意味着该响应将是两种类型中的任何一种

from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class BaseItem(BaseModel):
    description: str
    type: str


class CarItem(BaseItem):
    type: str = "car"


class PlaneItem(BaseItem):
    type: str = "plane"
    size: int


items = {
    "item1": {"description": "All my friends drive a low rider", "type": "car"},
    "item2": {
        "description": "Music is my aeroplane, it's my aeroplane",
        "type": "plane",
        "size": 5,
    },
}


@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
    return items[item_id]

item_id传item1

{
  "description": "All my friends drive a low rider",
  "type": "car"
}

item_id传item2

{
  "description": "Music is my aeroplane, it's my aeroplane",
  "type": "plane",
  "size": 5
}