一、基礎
響應模型與請求體模型類似,請求體就是通過Pydantic創建請求體模型,可用於對請求內容進行校驗,響應模型就是對響應體進行校驗。可以在任意的路徑操作中使用response_model參數來聲明響應的模型:
1、基本模型
from typing import Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr app = FastAPI() class UserIn(BaseModel): username: str password: str email: EmailStr address: str = None full_name: Optional[str] = None class UserOut(BaseModel): username: str email: EmailStr address: str = None full_name: Optional[str] = None @app.post("/user/", response_model=UserOut) async def create_user(user: UserIn): return user
上面定義了兩個Pydantic模型,一個為請求體模型,另一個則是響應體模型。響應體模型在路徑操作中進行聲明,所以響應體數據會經過這個模型校驗,返回的數據是符合這個模型的數據。顯然響應體模型過濾掉了password字段。
2、response_model_exclude_unset
該參數的作用是表示默認值不包含在響應中,僅包含實際給的值,如果實際給的值與默認值相同也會包含在響應中。
... @app.post("/user/", response_model=UserOut,response_model_exclude_unset=True) async def create_user(user: UserIn): return user ...
此時如果發送的請求體內容是:
{ "username": "zhangsan", "password": "123456", "email": "user@example.com" }
顯然address和full_name的字段值都是默認 的None,這樣響應體不會返回這些默認值,實際響應內容為:
{ "username": "zhangsan", "email": "user@example.com" }
3、response_model_exclude
顧名思義,該參數是排除響應模型中返回數據的字段:
... @app.post("/user/", response_model=UserOut, response_model_exclude=["address"]) async def create_user(user: UserIn): return user ...
請求體:
{ "username": "zhangsan", "password": "123456", "email": "user@example.com", "address": "張三村", "full_name": "張三" }
響應體中已經排除了address字段:
{ "username": "zhangsan", "email": "user@example.com", "full_name": "張三" }
4、response_model_include
該字段表示響應模型應該包含的字段:
... @app.post("/user/", response_model=UserOut, response_model_include=["address"]) async def create_user(user: UserIn): return user ...
請求體:
{ "username": "zhangsan", "password": "123456", "email": "user@example.com", "address": "張三村", "full_name": "張三" }
響應體中只包含address字段:
{ "address": "張三村" }
注意:response_model_exclude和response_model_include的值本質是將列表轉成了set,所以如果使用這樣response_model_include={"address"}的格式完全沒問題。
二、進階
1、多模型配合
一個業務開發需要配合多個模型,比如創建一個新用戶的操作:
from typing import Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr app = FastAPI() class UserIn(BaseModel): username: str password: str email: EmailStr address: str = None full_name: Optional[str] = None class UserOut(BaseModel): username: str email: EmailStr address: str = None full_name: Optional[str] = None class UserInDB(BaseModel): username: str hashed_password: str email: EmailStr address: str = None fullname: Optional[str] = None def fake_password_hasher(raw_password: str): return "secret" + 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) 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
在這個過程中:
- 接受客戶端的用戶請求體信息
- 對請求體內容進行校驗
- 對請求體的密碼進行hash加密
- 對數據庫用戶模型進行校驗
- 獲取符合數據庫用戶模型的數據,並且存入數據庫
- 向客戶端返回這個已經創建的用戶信息
2、**user_in.dict()
如果創建一個Pydantic模型,那么怎么創建一個模型對象呢?
... user_in = UserIn(username="zhang san", password="123456", email="user@example.com", address="zhang san street", full_name="zhangsan") ...
這樣就創建了一個UserIn模型的對象,顯然FastAPI中聲明UserIn類型,這樣會將請求體轉為Pydantic定義的UserIn的模型對象,也就是實現了上述過程。那么通過user_in.dict()又做了什么呢?顧名思義就是將上述user_in對象轉成key-value的字典格式,就是一個dict數據類型。
{ 'username': 'zhangsan', 'password': '123456', 'email': 'user@example.com', 'address': 'zhangsanstreet', 'full_name': 'zhangsan' }
如果現在有這樣一個字典類型的數據,如何傳遞給Pydantic的模型類,使其創建一個模型類對象,Python使用了字典(dict)解包的方式。所以對於:
UserInDB(**user_in.dict(), hashed_password=hashed_password)
通過**來對字典解包,形成的結果就是:
UserInDB( username='zhangsan', password='123456', email='user@example.com', address='zhangsanstreet', full_name='zhangsan', hashed_password=hashed_password )
但是通過UserInDB模型類的校驗后生成的最終結果就是:
UserInDB( username='zhangsan', hashed_password=hashed_password, email='user@example.com', address='zhangsanstreet', full_name='zhangsan' )
注意:上述通過dict()方法可以將Pydantic模型對象轉成字典方式,另外也可以通過jsonable_encoder()方法來實現:

from typing import Optional from fastapi import FastAPI from pydantic import BaseModel, EmailStr from fastapi.encoders import jsonable_encoder app = FastAPI() class UserIn(BaseModel): username: str password: str email: EmailStr address: str = None full_name: Optional[str] = None @app.post("/user/") async def create_user(user: UserIn): json_compatible_data = jsonable_encoder(user) print(type(json_compatible_data)) # <class 'dict'> return user
3、Union/List
- Union
from typing import Optional, Union, List ... @app.post("/response/model", response_model=Union[UserIn, UserOut]) async def response_model(user: UserIn): return user ...
Union的作用就是將Union中模型的字段取並集,顯然,如果只是UserOut模型,結果是不會返回password字段,但是把UserIn模型也放入Union中,取兩個模型並集后的字段作為響應模型進行校驗響應的user數據。
- List
... @app.post("/response/model", response_model=List[UserOut]) async def response_model(): user_list = [ { 'username': 'zhangsan', 'password': '123456', 'email': 'user@example.com', 'address': 'zhangsanstreet', 'full_name': 'zhangsan' }, { 'username': 'lisi', 'password': '123456', 'email': 'user@example.com', 'address': 'lisistreet', 'full_name': 'lisi' } ] return user_list ...
這樣響應的內容是由對象構成的列表。其響應的內容:
[ { "username": "zhangsan", "email": "user@example.com", "address": "zhangsanstreet", "full_name": "zhangsan" }, { "username": "lisi", "email": "user@example.com", "address": "lisistreet", "full_name": "lisi" } ]
4、dict構成響應
當不聲明任何響應的Pydantic模型時,此時並不知道有效的字段,那么可以通過聲明普通的鍵值對dict類型:
from typing import Dict from fastapi import FastAPI app = FastAPI() @app.get("/apple/dict/", response_model=Dict[str, float]) async def apple_dict(): return {"apple": 3.14, "pear": 5.12}