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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    python Database Poll for SQL SERVER
    SQLAlchemy表操作和增删改查
    flask动态url规则
    flask配置管理
    一个Flask运行分析
    Function Set in OPEN CASCADE
    Happy New Year 2016
    Apply Newton Method to Find Extrema in OPEN CASCADE
    OPEN CASCADE Multiple Variable Function
    OPEN CASCADE Gauss Least Square
  • 原文地址:https://www.cnblogs.com/shenjianping/p/14852051.html
Copyright © 2011-2022 走看看