zoukankan      html  css  js  c++  java
  • FastAPI响应系列(一) 响应模型

    一、基础

      响应模型与请求体模型类似,请求体就是通过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
    View Code

    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}

     

    作者:iveBoy
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——28
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——30
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——28
    小议如何改变指针的指向
    X Window Troubleshooting
    终于把傅雷的《世界美术名作二十讲》看完了
  • 原文地址:https://www.cnblogs.com/shenjianping/p/14852051.html
Copyright © 2011-2022 走看看