首页 > 其他分享 >fastapi

fastapi

时间:2024-09-24 16:13:56浏览次数:1  
标签:None name fastapi app item str id

fastapi

https://fastapi.tiangolo.com/zh/learn/

0 快速使用

# 异步框架
from fastapi import FastAPI
from pydantic import BaseModel
app=FastAPI()


class Item(BaseModel):
    name: str
    age:int

@app.get('/')
async def index():
    return {'code':100,'msg':'成功'}


@app.post('/login')
async def login(item:Item):
    print(item.name)
    print(item.age)
    return item

if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app='第一个fastapi:app',port=8001)

1 路径参数

from fastapi import FastAPI

app = FastAPI()


# 1 路径参数---基本使用
@app.get('/home/{id}')
async def home(id: str):
    return {'code': 100, 'id': id}


# 2 路径覆盖-->路由从上往下执行
# 这个永远不会走到
@app.get("/users/me")
async def read_user_me():
    return {"user_id": "the current user"}


@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}


# 3 枚举类型
from enum import Enum
class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    # 1 比较枚举元素

    # 2 获取枚举类型的值
    # 使用 model_name.value 或 your_enum_member.value 获取实际的值
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}

    if model_name.value == "lenet":
        return {"model_name": model_name, "message": "LeCNN all the images"}
    # 3 返回枚举类型,直接返回对象即可
    print(type(model_name))
    return {"model_name": model_name, "message": "Have some residuals"}


#  4 路径转换器 ############
# 参数名为 file_path,结尾部分的 :path 说明该参数应匹配路径
# /home/johndoe/myfile.txt 的路径参数要以斜杠(/)开头
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
    return {"file_path": file_path}

if __name__ == '__main__':
    import uvicorn

    uvicorn.run('路径参数:app', reload=True, port=8080)

2 查询参数

# 除了路径参数以外的,都是查询参数
@app.get("/items2/{item_id}")
async def read_item2(item_id: str, q: str|None = None, short: bool = False):
# 如果有多个路径参数,会依次排,后续都是查询参数
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
    user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False
):

from fastapi import FastAPI
from typing import Union
app = FastAPI()
# 1 查询参数---》如果在路径中没加 {}--->视图函数中得所有参数,都是请求参数
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]


@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
    return fake_items_db[skip: skip + limit]


# 2 可选参数  q: str | None = None  :路径参数+查询参数,查询参数可以不传--->路径和查询参数跟视图函数变量位置无关,但是一般咱们会把路径参数写前面
@app.get("/items1/{item_id}")
# async def read_item1(item_id: str, q: str | None = None):
async def read_item1(q: str | None, item_id: str):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

####3 查询参数类型转换 ######
@app.get("/items2/{item_id}")
async def read_item2(item_id: str, q: str|None = None, short: bool = False):
    item = {"item_id": item_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update(
            {"description": "This is an amazing item that has a long description"}
        )
    return item


######## 4 多个路径和查询参数##########
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
    user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False
):
    item = {"item_id": item_id, "owner_id": user_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update(
            {"description": "This is an amazing item that has a long description"}
        )
    return item


#### 5 必选查询参数 ####
@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
    #1 没设置默认值,参数必须填
    item = {"item_id": item_id, "needy": needy}
    return item

@app.get("/items3/{item_id}")
async def read_user_item3(
    item_id: str, needy: str, skip: int = 0, limit: Union[int, None] = None
):
    # 2 skip 和limit可以不填
    '''
    needy,必选的 str 类型参数
    skip,默认值为 0 的 int 类型参数,可以不填
    limit,可选的 int 类型参数
    '''
    item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
    return item


if __name__ == '__main__':
    import uvicorn

    uvicorn.run('查询参数:app', reload=True, port=8081)

3 请求体

from fastapi import FastAPI
from typing import Union
from pydantic import BaseModel
app = FastAPI()


# 定义一个请求体结构:类
class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
# 1 基本使用
@app.post("/items/")
async def create_item(item: Item): #如果参数 不是路径参数,而是使用BaseModel定义的类,都是请求体参数,其他的才是查询参数
    print(type(item))
    print(item.name)
    return item  # 直接返回对象,之间做序列化---》继承BaseModel


# 2 使用模型 在路径操作函数内部直接访问模型对象的属性
@app.post("/items1/")
async def create_item(item: Item):
    item_dict = item.dict() # 对象转字典  item.model_dump
    if item.tax:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict  # 返回字典

# 3 请求体 + 路径参数
@app.put("/items2/{item_id}")
async def update_item2(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}


# 4 请求体 + 路径参数 + 查询参数
'''
函数参数按如下规则进行识别:
路径中声明了相同参数的参数,是路径参数
类型是(int、float、str、bool 等)单类型的参数,是查询参数
类型是 Pydantic 模型的参数,是请求体
'''
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: Union[str, None] = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result
if __name__ == '__main__':
    import uvicorn

    uvicorn.run('请求体:app', reload=True, port=8081)

4 查询参数和字符串校验

from fastapi import FastAPI, Query,Path,Body
from typing import Union
from pydantic import BaseModel
app = FastAPI()

# 1 查询参数 q 的类型为 str,默认值为 None,因此它是可选的
@app.get("/items/")
async def read_items(q: Union[str, None] = None):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results


# 2 约束条件:即使 q 是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度
@app.get("/items1/")
async def read_items1(q: Union[str, None] = Query(default=None, max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 3 约束条件:即使 q 是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度,最短3
@app.get("/items2/")
async def read_items2(q: Union[str, None] = Query(default=None, min_length=3,max_length=50)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 4 添加正则表达式
@app.get("/items3/")
async def read_items3(
    q: Union[str, None] = Query(
        default=None, min_length=3, max_length=50, pattern="^fixedquery$"
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 5 默认值
@app.get("/items4/")
async def read_items4(q: str = Query(default="fixedquery", min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 6 声明为必需参数
@app.get("/items5/")
async def read_items5(q: str = Query(min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results


# 7 使用省略号(...)声明必需参数
'''
如果你之前没见过 ... 这种用法:它是一个特殊的单独值,它是 Python 的一部分并且被称为「省略号」。 Pydantic 和 FastAPI 使用它来显式的声明需要一个值
'''
@app.get("/items6/")
async def read_items6(q: str = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 8 使用None声明必需参数
@app.get("/items7/")
async def read_items7(q: Union[str, None] = Query(default=..., min_length=3)):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 9 使用Pydantic中的Required代替省略号(...)
# from pydantic import Required  # v2 版本的pydantic 移除了该参数
# @app.get("/items/")
# async def read_items(q: str = Query(default=Required, min_length=3)):
#     results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
#     if q:
#         results.update({"q": q})
#     return results

# 10 查询参数列表 / 多个值
from typing import List, Union
@app.get("/items8/")
async def read_items8(q: Union[List[str], None] = Query(default=None)):
    query_items = {"q": q}
    return query_items
# http://localhost:8000/items/?q=foo&q=bar
# 要声明类型为 list 的查询参数,如上例所示,你需要显式地使用 Query,否则该参数将被解释为请求体


# 11 具有默认值的查询参数列表 / 多个值
from typing import List, Union
@app.get("/items9/")
async def read_items9(q: List[str] = Query(default=["foo", "bar"])):
    query_items = {"q": q}
    return query_items

# 12 也可以直接使用 list 代替 List [str]
@app.get("/items10/")
async def read_items10(q: list = Query(default=[])):
    query_items = {"q": q}
    return query_items

# 13 声明更多元数据  title   description ---  OpenAPI 的支持程度可能不同
@app.get("/items11/")
async def read_items11(
    q: Union[str, None] = Query(default=None, title="Query string", description="这是描述新消息",min_length=3),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 14 别名参数
#http://127.0.0.1:8000/items/?item-query=foobaritems
# item-query 不是一个有效的 Python 变量名称
#最接近的有效名称是 item_query。
#但是你仍然要求它在 URL 中必须是 item-query...
#这时你可以用 alias 参数声明一个别名,该别名将用于在 URL 中查找查询参数值
@app.get("/items12/")
async def read_items12(q: Union[str, None] = Query(default=None, alias="item-query")):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

# 15 弃用参数
# 现在假设你不再喜欢此参数。
# 你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为已弃用。
# 那么将参数 deprecated=True 传入 Query弃用参数
@app.get("/items13/")
async def read_items13(
    q: Union[str, None] = Query(
        default=None,
        alias="item-query",
        title="Query string",
        description="Query string for the items to search in the database that have a good match",
        min_length=3,
        max_length=50,
        pattern="^fixedquery$",
        deprecated=True,
    ),
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

if __name__ == '__main__':
    import uvicorn

    uvicorn.run('查询参数校验:app', reload=True, port=8081

5 路径参数和数值校验

from fastapi import FastAPI, Path, Query
from typing import Annotated
from typing import Union
from pydantic import BaseModel

app = FastAPI()


#1 Path 路径函数,声明元数据,可以声明与 Query 相同的所有参数
	# 路径参数总是必需的,因为它必须是路径的一部分。
	# 即使你使用 None 声明路径参数或设置一个其他默认值也不会有任何影响,它依然会是必需参数
	# python 3.10版本,使用Annotated


@app.get("/items/{item_id}")
async def read_items(
        # item_id:int= Path(title="The ID of the item to get") Annotated 第一个参数是类型,第二个参数是等于谁
        item_id: Annotated[int, Path(title="The ID of the item to get")],
        q: Annotated[str | None, Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results


# python 3.8版本,不使用Annotated
@app.get("/items/{item_id}")
async def read_items(
        item_id: int = Path(title="The ID of the item to get"),
        q: Union[str, None] = Query(default=None, alias="item-query"),
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results


# 2 按需对参数排序
'''
假设你想要声明一个必需的 str 类型查询参数 q。
而且你不需要为该参数声明任何其他内容,所以实际上你并不需要使用 Query。
但是你仍然需要使用 Path 来声明路径参数 item_id。
如果你将带有「默认值」的参数放在没有「默认值」的参数之前,Python 将会报错。
但是你可以对其重新排序,并将不带默认值的值(查询参数 q)放到最前面。
对 FastAPI 来说这无关紧要。它将通过参数的名称、类型和默认值声明(Query、Path 等)来检测参数,而不在乎参数的顺序
'''


@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results


# 3 按需对参数排序的技巧
'''
如果你想不使用 Query 声明没有默认值的查询参数 q,同时使用 Path 声明路径参数 item_id,并使它们的顺序与上面不同,Python 对此有一些特殊的语法。
传递 * 作为函数的第一个参数。
Python 不会对该 * 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对),也被称为 kwargs,来调用。即使它们没有默认值
'''


@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results


# 4 数值校验(int,float):大于等于,大于,小于等于
'''
使用 Query 和 Path(以及你将在后面看到的其他类)可以声明字符串约束,但也可以声明数值约束。
像下面这样,添加 ge=1 后,item_id 将必须是一个大于(greater than)或等于(equal)1 的整数
gt:大于(greater than)
ge:大于等于(greater than or equal)
lt:小于(less than)
le:小于等于(less than or equal)
数值校验同样适用于 float 值
'''


@app.get("/items/{item_id}")
async def read_items(
        *, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results

if __name__ == '__main__':
    import uvicorn

    uvicorn.run('路径参数数据校验:app', reload=True, port=8081)

6 请求体多个参数

from fastapi import FastAPI, Path, Query, Body
from typing import Annotated
from typing import Union
from pydantic import BaseModel

app = FastAPI()


# 1 混合使用 Path、Query 和请求体参数
# 通过将默认值设置为 None 来将请求体参数声明为可选参数
class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


@app.put("/items/{item_id}")
async def update_item(
        item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
        q: str | None = None,
        item: Item | None = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results


# 2 多个请求体参数
'''
{
  "item": {
    "name": "string",
    "description": "string",
    "price": 0,
    "tax": 0
  },
  "user": {
    "username": "string",
    "full_name": "string"
  }
}

'''


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


class User(BaseModel):
    username: str
    full_name: str | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
    results = {"item_id": item_id, "item": item, "user": user}
    return results


# 3 请求体中的单一值 Body 使用
@app.put("/items3/{item_id}")
async def update_item3(
        item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results


# 4 多个请求体参数和查询参数 Body 同样具有与 Query、Path 以及其他后面将看到的类完全相同的额外校验和元数据参数。
@app.put("/items/{item_id}")
async def update_item(
        *,
        item_id: int,
        item: Item,
        user: User,
        importance: Annotated[int, Body(gt=0)],
        q: str | None = None,
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results


# 5 嵌入单个请求体参数
'''
{
  "item": {
    "name": "string",
    "description": "string",
    "price": 0,
    "tax": 0
  }
}
而不是
{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2
}

'''


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
    results = {"item_id": item_id, "item": item}
    return results


if __name__ == '__main__':
    import uvicorn

    uvicorn.run('请求体多个参数:app', reload=True, port=8081)

7 请求体字段校验

from fastapi import FastAPI, Path, Query,Body
from typing import Annotated
from typing import Union
from pydantic import BaseModel ,Field
app = FastAPI()

# 1 使用 pydantic的 Field,声明模型属性
class Item(BaseModel):
    name: str
    description: str | None = Field(
        default=None, title="The description of the item", max_length=300
    )
    price: float = Field(gt=0, description="The price must be greater than zero")
    tax: float | None = None


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
    results = {"item_id": item_id, "item": item}
    return results

8 请求体嵌套

from fastapi import FastAPI, Path, Query, Body
from typing import Annotated
from typing import Union,List
from pydantic import BaseModel, Field, HttpUrl
from typing import List, Union

app = FastAPI()

# 1 List 字段
# tags 成为一个由元素组成的列表。不过它没有声明每个元素的类型
'''
{
  "name": "string",
  "description": "string",
  "price": 0,
  "tax": 0,
  "tags": []
}
'''


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: list = []


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results


# 2 具有子类型的 List 字段
class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None
    tags: List[str] = []


# 3 Set 类型 标签不应该重复,它们很大可能会是唯一的字符串
'''
即使你收到带有重复数据的请求,这些数据也会被转换为一组唯一项。
而且,每当你输出该数据时,即使源数据有重复,它们也将作为一组唯一项输出
'''

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()

# 4 嵌套模型
'''
  "name": "string",
  "description": "string",
  "price": 0,
  "tax": 0,
  "tags": [],
  "image": {
    "url": "string",
    "name": "string"
  }
  "images": [{
    "url": "string",
    "name": "string"
  },{
    "url": "string",
    "name": "string"
  }
  
  ]
}
'''


class Image(BaseModel):
    url: str
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None
    images: List[Image] | None = None


# 5 特殊的类型和校验
# Pydantic 的外部类型 的文档 https://docs.pydantic.dev/latest/concepts/types/#handling-custom-generic-classes
# 在 Image 模型中我们有一个 url 字段,我们可以把它声明为 Pydantic 的 HttpUrl,而不是 str
class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    image: Image | None = None


# 6 有一组子模型的属性 
'''
{
    "name": "Foo",
    "description": "The pretender",
    "price": 42.0,
    "tax": 3.2,
    "tags": [
        "rock",
        "metal",
        "bar"
    ],
    "images": [
        {
            "url": "http://example.com/baz.jpg",
            "name": "The Foo live"
        },
        {
            "url": "http://example.com/dave.jpg",
            "name": "The Baz"
        }
    ]
}
'''


class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None  # 有一组子模型的属性 


# 6 深度嵌套 请注意 Offer 拥有一组 Item 而反过来 Item 又是一个可选的 Image 列表是如何发生的
class Image(BaseModel):
    url: HttpUrl
    name: str


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None
    tags: set[str] = set()
    images: list[Image] | None = None


class Offer(BaseModel):
    name: str
    description: str | None = None
    price: float
    items: list[Item]


@app.post("/offers/")
async def create_offer(offer: Offer):
    return offer


# 7 纯列表请求体
class Image(BaseModel):
    url: HttpUrl
    name: str


@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):  # 纯列表请求体
    return images


# 8 任意 dict 构成的请求体
'''
请记住 JSON 仅支持将 str 作为键。
但是 Pydantic 具有自动转换数据的功能。
这意味着,即使你的 API 客户端只能将字符串作为键发送,只要这些字符串内容仅包含整数,Pydantic 就会对其进行转换并校验。
然后你接收的名为 weights 的 dict 实际上将具有 int 类型的键和 float 类型的值
'''

@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
    return weights

9 模式的额外信息和其他字段

from fastapi import FastAPI, Path, Query,Body
from typing import Annotated
from typing import Union
from pydantic import BaseModel ,Field,HttpUrl
from typing import List, Union
app = FastAPI()

# 1 Pydantic schema_extra
class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

    # 这些额外的信息将按原样添加到输出的JSON模式中,作为api的提示
    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ]
        }
    }


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
  
# 2 Field 的附加参数
 ## 在 Field, Path, Query, Body 和其他你之后将会看到的工厂函数,你可以为JSON 模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON 模式声明额外信息,比如增加 example
class Item(BaseModel):
    name: str = Field(examples=["Foo"])
    description: str | None = Field(default=None, examples=["A very nice Item"])
    price: float = Field(examples=[35.4])
    tax: float | None = Field(default=None, examples=[3.2])


@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    results = {"item_id": item_id, "item": item}
    return results
  
# 3 Body 额外参数
class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
    item_id: int,
    item: Annotated[
        Item,
        Body(
            examples=[
                {
                    "name": "Foo",
                    "description": "A very nice Item",
                    "price": 35.4,
                    "tax": 3.2,
                }
            ],
        ),
    ],
):
    results = {"item_id": item_id, "item": item}
    return results

10 form表单

# pip install python-multipart
import uvicorn
from fastapi import FastAPI,Form,Body

app = FastAPI()

@app.post("/login/")
async def login(username: str = Form(), password: str = Form()):
    return {"username": username}

if __name__ == '__main__':
    uvicorn.run("form表单:app", port=8080)

11 文件上传

import uvicorn
from typing import List
from fastapi import FastAPI,Form,File,UploadFile

app = FastAPI()

# 小文件上传
@app.post("/file/")
async def create_file(file: bytes = File()):
    print(file) #字节流数据,小文件适合
    with open('a.ini','wb') as f:
        f.write(file)
    return {"file_size": len(file)}
# 大文件上传
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    print(file) #文件句柄方式,适合大文件
    # with open(file.filename,'wb') as f:
    #     data = await file.read()
    #     f.write(data)

    with open(file.filename, "wb") as buffer:
        while True:
            data = await file.read(1024)
            if not data:
                break
            buffer.write(data)
    return {"filename": file.filename}

# 多文件上传
@app.post("/files/")
async def create_files(files: List[bytes] = File()):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile]):
    return {"filenames": [file.filename for file in files]}
if __name__ == '__main__':
    uvicorn.run("s:app", host="127.0.0.1", port=5000,reload=True)


12 Request对象

import uvicorn
from typing import List
from fastapi import FastAPI,Request

app = FastAPI()

# 小文件上传
@app.post("/demo/{item_id}")
async def demo(item_id:int,request:Request,q:str=None):
    print(item_id)
    print(q)
    print(request)
    # 1 cookie
    print(request.cookies)
    # 2 header:user-agent
    print(request.headers)  # user-agent在里面
    print(request.headers.get('user-agent'))
    # 3 url
    print(request.url)
    # 4 请求体数据
    from starlette.requests import Request
    # data = await request.body()
    # data = await request.body()
    data = await request.json()
    print(data)

    # 5 客户端ip地址和端口
    print(request.client.host)
    print(request.client.port)

    # 6 请求方式
    print(request.method)



    return 'ok'

if __name__ == '__main__':
    uvicorn.run("request对象:app", host="127.0.0.1", port=5000,reload=True)


13 前端传的cookie和Header

# 1 基本使用
##3.9+ Annotated
from typing import Annotated, Union
from fastapi import Cookie, FastAPI,Request,Header
import uvicorn
app = FastAPI()
'''
python 3.8  --没有Annotated
async def read_items(ads_id: Union[str, None]= Cookie(default=None)):
python 3.9 --有Annotated
async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None):
python 3.10 --有Annotated 有 |
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
'''
@app.get("/items/")
async def read_items(ads_id: Annotated[Union[str, None], Cookie()] = None):
    return {"ads_id": ads_id}

'''
Cookie 、Path 、Query 是兄弟类,都继承自共用的 Param 类。

注意,从 fastapi 导入的 Query、Path、Cookie 等对象,实际上是返回特殊类的函数。

必须使用 Cookie 声明 cookie 参数,否则该参数会被解释为查询参数
'''

# postman 访问,在请求头的cookie参数中写入 ads_id=9001


# 2 同样可以在request对象中取出来
@app.get("/items1/")
async def read_items1(request:Request,ads_id: Annotated[Union[str, None], Cookie()] = None):
    c=request.cookies
    print(c)
    return {"ads_id": ads_id}

if __name__ == '__main__':
    uvicorn.run("获取cookie:app", host="127.0.0.1", port=5000,reload=True)

14 响应模型参数

就是序列化类和反序列化类不使用同一个

read_only和write_only的控制

import uvicorn
from typing import List,Union,Any
from pydantic import BaseModel,EmailStr
from fastapi import FastAPI,Form,File,UploadFile,Request

app = FastAPI()


# 1 响应跟传入的相同
class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: Union[str, None] = None

class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: Union[str, None] = None

# 2 不指定
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
    return user


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

# 4 response_model_exclude_unset 使用  使用 response_model_exclude_unset 来仅返回显式设定的值
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]
# 5 还可以使用:
# '''
# response_model_exclude_defaults=True
# response_model_exclude_none=True
# response_model_include
# response_model_exclude
# '''
@app.get("/items1/{item_id}", response_model=Item, response_model_exclude=['name'])
async def read_item1(item_id: str):
    return items[item_id]

if __name__ == '__main__':
    uvicorn.run("响应模型参数:app", host="127.0.0.1", port=5000,reload=True)


15 配置文件

15.1 settings.py方案

完全自己定义,导入使用

15.2 dotenv 方案

# 1 安装python-dotenv
# python-dotenv 是一个Python库,用于从文本文件中加载环境变量
pip3 install python-dotenv
# 2 安装pydantic_settings
pip3 install pydantic_settings
# 3 编写 .env
# -------- 服务配置信息 --------
APP_PORT=8080
APP_HOST=0.0.0.0
APP_VERSION=v1.0.0
APP_DEBUG=true

# -------- 数据库配置 --------
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=123
DB_DATABASE=demo001

# -------- redis配置 --------
REDIS_DSN=redis://127.0.0.1:6379/0
        
# 4 编写配置模型 config.py

from pydantic import RedisDsn
from pydantic_settings import BaseSettings
from dotenv import load_dotenv


class AppConfigSettings(BaseSettings):
    """应用配置"""
    app_port: int = 8080
    app_env: str = "dev"
    app_debug: bool = False
    app_host: str
    """数据库配置"""
    db_host: str
    db_port: int
    db_user: str
    db_password: str
    db_database: str
    """redis配置"""
    redis_dsn: RedisDsn = None


# 执行---》把 .env 中配置--》映射到 配置类中AppConfigSettings
load_dotenv()
# 实例化配置模型
appSettings = AppConfigSettings()

        
        
# 5 main.py 中使用
import uvicorn
from fastapi import FastAPI
app = FastAPI()
from config import appSettings  # 天然单例
@app.get("/")
async def root():
    print(appSettings.db_database)
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run("配置文件:app",reload=True,port=appSettings.app_port,host=appSettings.app_host)



16 路由分发

# fastapi APIRouter--》路由分发--》目录划分
	-1 实例化得到 APIRouter对象
    -2 在app中注册
    -3 以后写路径都用,APIRouter对象注册路径即可

1 小型项目

"""
├── fastapi_x
	├── src/				# 项目运行时/开发时日志目录 - 包
		├── home.py   	
		├── models.py   	# 表模型
		└── users.py  		
	├── .env 		        # 其他配置信息
    ├── main.py 		    # 脚本文件
    └── settings.py    		# 配置文件
"""

main.py

from fastapi import FastAPI
from src.home import home
from src.users import user
app = FastAPI()

app.include_router(home,prefix='/api/v1/home',tags=['主页相关接口'])
app.include_router(user,prefix='/api/v1/user',tags=['用户相关接口'])

if __name__ == '__main__':
    import uvicorn
    uvicorn.run('main:app',reload=True)

settings.py

from pydantic import RedisDsn
from pydantic_settings import BaseSettings
from dotenv import load_dotenv
class AppConfigSettings(BaseSettings):
    """应用配置"""
    app_port: int = 8080
    app_env: str = "dev"
    app_debug: bool = False
    """数据库配置"""
    db_host: str
    db_port: int
    db_user: str
    db_password: str
    db_database: str
    """redis配置"""
    redis_dsn: RedisDsn = None

# 加载 .env 文件
load_dotenv()
# 实例化配置模型
settings = AppConfigSettings()

.env

# -------- 服务配置信息 --------
APP_PORT=8080
APP_HOST=0.0.0.0
APP_VERSION=v1.0.0
APP_DEBUG=true

# -------- 数据库配置 --------
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=123
DB_DATABASE=lqz_mysql

# -------- redis配置 --------
REDIS_DSN=redis://127.0.0.1:6379/0

home.py

from fastapi import APIRouter
from settings import settings

home = APIRouter()


@home.get("/")
async def root():
    print(settings.db_database)
    return {"message":settings.db_database}


@home.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello {name}"}

users.py

from fastapi import APIRouter

user = APIRouter()


@user.get("/users")
async def root():
    return {"message": "返回所有用户"}

2 大型项目

"""
├── fastapi_x
	├── apps/				# 项目运行时/开发时日志目录 - 包
		├── home/  	
		├── users/
		...
	├── .env 		        # 其他配置信息
    ├── main.py 		    # 脚本文件
    └── settings.py    		# 配置文件
"""

17 中间件

import asyncio
import time

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()


# @app.middleware("http")
# async def add_process_time_header(request: Request, call_next):
#     # 1 统计时间---》process_request
#     start_time = time.time()
#     # 2 继续执行下面的中间件---》继续执行视图函数
#     response = await call_next(request)
#     # 3 视图函数执行完,取了时间--》process_response
#     process_time = time.time() - start_time
#     response.headers["X-Process-Time"] = str(process_time)
#     return response

# 请求头中必须带token,如果不带,直接返回
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    token = request.headers.get('token', None)
    if token:
        response = await call_next(request)
        
        return response
    else:
        return JSONResponse({'code': 101, 'msg': '没有携带token'})


@app.get('/index')
async def index():
    await asyncio.sleep(1)  # 模拟io
    return 'index'


if __name__ == '__main__':
    import uvicorn

    uvicorn.run('中间件:app', reload=True, port=8888)

18 cors

import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
    "http://localhost.tiangolo.com",
    "https://localhost.tiangolo.com",
    "http://localhost",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins='*',  #  * 表示所有
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/")
async def main():
    return {"message": "Hello World"}

if __name__ == '__main__':
    uvicorn.run("cors:app", host="127.0.0.1", port=5000,reload=True)

19 模板和静态文件

import uvicorn
from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles

app = FastAPI()
# 静态文件
app.mount("/static", StaticFiles(directory="static"))
# 模板
templates = Jinja2Templates(directory="templates")


@app.get("/index")
def index(request: Request):
    context = {
        'request': request,
        'name': 'lqz',
        "age": 19,
        "hobby": ["篮球", "足球"],
        "info": {
            "city": "上海",
            "address": "浦东"
        }
    }
    return templates.TemplateResponse('index.html', context)


if __name__ == '__main__':
    uvicorn.run("静态文件和模板:app", host="127.0.0.1", port=5000,reload=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>{{name}} ({{age}})</h1>
<ul>
    {% for item in hobby%}
    <li>{{item}}</li>
    {% endfor %}
</ul>
<div>
    <ul>
        {% for k,v in info.items()%}
        <li>{{k}}{{v}}</li>
        {% endfor %}
    </ul>
</div>
<img src="/static/demo.png" style="width: 200px;">
</body>
</html>

20 ORM

# 0 官方文档:sqlalchemy,普遍用tortoise更多一些

# 1 文档
# github:https://github.com/tortoise/tortoise-orm
# 文档:https://tortoise.github.io/getting_started.html

# 2 安装
pip3 install tortoise-orm[asyncmy]

创建表

from tortoise import Model,fields

class Publish(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    addr = fields.CharField(max_length=255)
    def __str__(self):
        return self.name


class Book(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    publish = fields.ForeignKeyField('models.Publish', related_name='book')
    authors = fields.ManyToManyField('models.Author', related_name='books', through='author_book')

    def __str__(self):
        return self.name


class Author(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)

    def __str__(self):
        return self.name

main.py

from fastapi import FastAPI
from apps.home.views import home
from apps.order.views import order

from tortoise.contrib.fastapi import register_tortoise
from settings import TORTOISE_ORM
app = FastAPI()
# register_tortoise(app,
#                   db_url="mysql://root:[email protected]:3306/fastapi",
#                   modules={"models": ["apps.home.models","apps.order.models"]},  # models列表路径
#                   )
register_tortoise(app,config=TORTOISE_ORM)
app.include_router(home,prefix='/api/v1/home',tags=['home描述信息'])
app.include_router(order,prefix='/api/v1/order',tags=['order描述信息'])



settings.py

TORTOISE_ORM = {
    'connections': {
        'default': {
            # 'engine': 'tortoise.backends.asyncpg',  PostgreSQL
            'engine': 'tortoise.backends.mysql',  # MySQL or Mariadb
            'credentials': {
                'host': '127.0.0.1',
                'port': '3306',
                'user': 'root',
                'password': '123',
                'database': 'fastapi',
                'minsize': 1,
                'maxsize': 5,
                'charset': 'utf8mb4',
                "echo": True
            }
        },
    },
    # "connections": {"default": "mysql://root:[email protected]:3306/fastapi"},
    'apps': {
        'models': {
            # models:models 找到对应自定义的model.py
            'models': ['apps.home.models','apps.order.models', "aerich.models"],  # aerich.models迁移模型
            'default_connection': 'default',
        }
    },
    'use_tz': False,
    'timezone': 'Asia/Shanghai'
}

aerich迁移

# 0 pip3 install aerich
# 1. 打开命令行,切换到项目根目录 
cd 项目
# 2. 初始化配置项
aerich init -t settings.TORTOISE_ORM

    #初始化完成后会在当前目录生成一个文件pyproject.toml和一个文件夹migrations
    #· pyproject.toml: 保存配置文件路径
    #. migrations:存放.sql迁移文件 
    
# 3. 初始化数据库,一般情况下只用一次
aerich init-db
    
    #· 如果TORTOISE_ORM配置文件中的models改了名,执行这条命令时需要增加--app参数,来指定你修改的名字。
    #· 在migrations的指定app目录下生成sql文件(如果model不为空时),并在数据库中生成表。

    
# 4. 更新模型并进行迁移---更新使用
aerich migrate --name any_name
    
    #· 每次修改model后执行此命令,将在migrations文件夹下生成.sql迁移文件;
    #· --name参数为你的迁移文件添加备注,默认为update;
    #· 迁移文件名的格式为{version_num}{datetime}{any_name|update}.sql;
    #· 如果aerich识别到您正在重命名列,它会要求重命名{old_column}为{new_column} [True],您可以选择True
    #  重命名列而不删除列,或者选择False删除列然后创建,如果使用Mysql,只有8.0+支持重命名。
       
# 5. 更新最新修改到数据库
aerich upgrade    或aerich upgrade [xxx.sql]


# 6. 后续每次修改models文件内容(新增、删除或修改数据模型, 非models文件重命名),只需执行步骤4、5即可.

# 7. 其它操作
# 降级到指定版本
aerich downgrade -v 指定版本 -d 降级的同时删除迁移文件 --yes 确认删除,不再交互式输入

# 显示当前可以迁移的版本
aerich heads

# 显示迁移历史
aerich history

增删查改

# 查询所有
# books = await Book.all()
# 过滤
# books = await Book.filter(name='红楼')
# 模糊查询
# books = await Book.filter(name__contains='红')
# books = await Book.filter(id__gt=1).all()
# 查询值
# books = await Book.filter(name='红楼').values('name', 'id')

# 获取单个对象
# book = await Book.get(name='红楼')

# 多对多查询
books = await Book.filter(book__publish__name="北京出版社").values('name')

# 一对多查询
# books = await Book.filter(book__authors__name='张三').values('name')





# 多对多查询
books = await Book.filter(authors__name="张三").values('name')

# 一对多查询
books = await Book.filter(publish__name="北京出版社").values('name')
 # 方式一
book = Book(name='西游记', publish_id=1)
await book.save()

# 方式二
book = await Book.create(name='三国演义', publish_id=2)

# 多对多关系
book = await Book.get(name='三国演义')
publish = await Publish.get(name='北京出版社')
await book.publish.add(publish)
 # 方式一
book = await Book.get(name='红楼')
book.name = '红楼1'
await book.save()

# 方式二
await Book.filter(name='红楼').update(name='红楼梦')
 # 方式一
book = await Book.get(name='红楼')
await book.delete()

# 方式二
await Book.filter(name='红楼').delete()
https://blog.csdn.net/weixin_54217348/article/details/136772215
https://www.cnblogs.com/pearlcity/p/17829736.html
https://github.com/prostomarkeloff/fastapi-tortoise

21 websocket

from fastapi import FastAPI, WebSocket,Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
import uvicorn
app = FastAPI()
# 静态文件
app.mount("/static", StaticFiles(directory="static"))
# 模板
templates = Jinja2Templates(directory="templates")
html = """
<!DOCTYPE html>
<html>
    <head>
        <title>Chat</title>
    </head>
    <body>
        <h1>WebSocket Chat</h1>
        <form action="" onsubmit="sendMessage(event)">
            <input type="text" id="messageText" autocomplete="off"/>
            <button>Send</button>
        </form>
        <ul id='messages'>
        </ul>
        <script>
            var ws = new WebSocket("ws://localhost:8001/ws");
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById("messageText")
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>
"""


@app.get("/")
async def get():
    return HTMLResponse(html)

@app.get('/index')
def index(request:Request):
    context={
        'request':request
    }
    return templates.TemplateResponse('websocket.html',context)


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    count=0
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message text was: {data}")

if __name__ == '__main__':
    uvicorn.run("websocket:app", host="127.0.0.1", port=8001,reload=True)

标签:None,name,fastapi,app,item,str,id
From: https://www.cnblogs.com/unrealqcc/p/18429361

相关文章

  • 基于SqlAlchemy+Pydantic+FastApi的Python开发框架
    随着大环境的跨平台需求越来越多,对与开发环境和实际运行环境都有跨平台的需求,Python开发和部署上都是跨平台的,本篇随笔介绍基于SqlAlchemy+Pydantic+FastApi的Python开发框架的技术细节,以及一些技术总结。最近这几个月一直忙于Python开发框架的整合处理,将之前开发框架中很多重要......
  • fastapi-events fastapi 异步事件分发处理扩展
    fastapi-eventsfastapi异步事件分发处理扩展,提供了本地,以及远程消息处理能力,同时包含了一些内置的handler,对于自定义handler也是比较灵活的参考使用app.pyfromfastapiimportFastAPIfromfastapi.requestsimportRequestfromfastapi.responsesimportJSONResponsefrom......
  • LLMChat入门指南 - 基于Flutter和FastAPI的大语言模型聊天应用
    LLMChat-您的AI聊天助手......
  • 通过 NSSM 把 FastAPI+Celery+Flower 注册成 3个Windos 服务
    通过NSSM把FastAPI+Celery+Flower注册成3个Windos服务什么是nssm?是一个服务封装程序,它可以将普通exe程序封装成服务,实现开机自启动,同类型的工具还有微软自己的srvany,不过nssm更加简单易用,并且功能强大。nssm官网nssm常用命令nssminstallservername//创建servername......
  • fastapi-events fastapi 异步事件分发处理扩展
    fastapi-eventsfastapi异步事件分发处理扩展,提供了本地,以及远程消息处理能力,同时包含了一些内置的handler,对于自定义handler也是比较灵活的参考使用app.pyfromfastapiimportFastAPIfromfastapi.requestsimportRequestfromfastapi.responsesimport......
  • FastAPI 应用安全加固:HTTPSRedirectMiddleware 中间件全解析
    在当今的网络环境中,数据安全变得越来越重要。HTTPS作为一种安全协议,它通过加密传输数据来保护用户信息免受窃取和篡改。在FastAPI应用中,确保所有的HTTP请求都通过HTTPS进行是至关重要的。中间件在FastAPI中用于处理请求前后的通用任务,例如身份验证、日志记录、请......
  • 从单体到微服务:FastAPI ‘挂载’子应用程序的转变
    在现代Web应用开发中,模块化架构是一种常见的设计模式,它有助于将大型应用程序分解为更小、更易于管理的部分。FastAPI,作为一个高性能的PythonWeb框架,提供了强大的支持来实现这种模块化设计。通过“挂载”子应用程序,我们可以为不同的功能区域(如前端接口、管理员接口和用......
  • 深入理解FastAPI中的root_path:提升API部署灵活性的关键配置
    在Web开发领域,FastAPI因其高性能、易于使用和类型提示功能而备受开发者喜爱。然而,当涉及到在生产环境中部署FastAPI应用程序时,我们常常需要面对一些挑战,比如如何正确处理代理服务器添加的路径前缀。这时,root_path配置就变得至关重要。本文将深入探讨FastAPI中的root_path,并......
  • fastapi 使用package 提供web 静态资源
    日常开发中对于静态资源的处理方法很多,但是如果静态资源也可以像包一样集成发布就会比较方便对于我们实际软件部署就会比较方便以下是关于fastapi通过package提供静态资源参考代码安装静态资源我们使用了bootstrap4,基于这个包提供app.pyfromfastapiimportFastAPIfrom......
  • 【FastAPI】离线使用Swagger UI 或 国内网络如何快速加载Swagger UI
    在FastAPI中,默认情况下,当应用启动时,SwaggerUI会通过在线加载SwaggerUI的静态资源。这意味着如果应用运行在没有互联网连接的环境中,默认的Swagger文档页面将无法加载。为了在离线环境中使用SwaggerUI,你需要手动加载SwaggerUI的静态文件并将其与FastAPI集成。......