根据慕课网视频教程
地址:https://www.bilibili.com/video/BV1iN411X72b?p=36
print("\033[31m5. --- ORM模型: 从类创建符合的ORM对象模型 ---\033[Om")#显示彩色内容
fastapi是高性能的web框架。他的主要特点是:
快速编码
减少人为bug
直观
简易
具有交互式文档
基于API的开放标准(并与之完全兼容):OpenAPI(以前称为Swagger)和JSON Schema。
一、fastapi的安装
pip install fastapi
当然你可以使用国内阿里云镜像源进行安装,会快很多,上面的语句变成下面的:
pip install fastapi -i https://mirrors.aliyun.com/pypi/simple
因为fastapi启动依赖于uvicorn,所以我们还需要安装uvicorn
pip install uvicorn -i https://mirrors.aliyun.com/pypi/simple
到这里,fastapi就安装完毕了,下面我们来验证一下安装是否成功
新建名字叫main.py的文件,将下面内容复制到里面去
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
然后使用终端开启uvicorn
服务
uvicorn main:app --reload
uvicorn main:app 命令指:
main: main.py 文件(也可理解为Python模块).
app: main.py 中app = FastAPI()语句创建的app对象.
--reload: 在代码改变后重启服务器,只能在开发的时候使用
你将会看到如下的输出:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [8438] using statreload
INFO: Started server process [8440]
INFO: Waiting for application startup.
INFO: Application startup complete.
然后打开浏览器,输入: http://127.0.0.1:8000。看看有没有打印出:"message": "Hello World"
这句话,如果有,表示安装成功。
如果你此时访问 http://127.0.0.1:8000/docs。你将会看到自动生成的API交互文档。这点很重要,也是fastapi的一个优点。
fastapi项目启动时,提示ERROR: Error loading ASGI app. Could not import module “main”.
文件名为 f1.py
代码如下:
from fastapi import FastAPI # 导入FastAPI
import uvicorn
app = FastAPI() # 创建一个app实例
@app.get("/") # 编写一个路径操作装饰器
async def root(): # 编写一个路径操作函数
return {"你好!": "朋友。"}
if __name__ == '__main__':
uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True)
注意:声明app的文件路径应该是 f1:app,而不是main:app。
只需将 app=‘main:app’ 改为app='f1:app’即可。
结束!
# -*- codeing = utf-8 -*-
# @time : 2021/5/7 23:57
# @file : main.py.PY
# @Author : 夜羽
from fastapi import FastAPI # 导入FastAPI
#from fastapi import APIRouter #接口路由
import uvicorn # 运行fastapi
'''内容选填
app = FastAPI() # 创建一个app实例
#app01 = APIRouter()
@app.get("/") # 编写一个路径操作装饰器
async def root(): # 编写一个路径操作函数
return {"你好!": "朋友。123"}
'''
if __name__ == '__main__':
uvicorn.run(app='main:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
# workers=1进程数量
main: main.py 文件(也可理解为Python模块).
app: main.py 中app = FastAPI()语句创建的app对象.
--reload: 在代码改变后重启服务器,只能在开发的时候使用
debug: 修改代码后不必再运行一次程序,保存后可直接运行
另一种方法
导入运行文件名在一个程序文件中:
from .chapter03 import app03
from .chapter04 import app04
from .chapter05 import app05
from .文件名 import app实例名
后只用一个程序运行多个:
from fastapi import FastAPI
import uvicorn #运行fastapi
from fastapi知识点教程 import app03 导入app实例名称项目
from fastapi知识点教程 import app04
from fastapi知识点教程 import app05
#from 上层文件夹名称 import app实例名
app = FastAPI()
#prefix= 路径 tags=[] 名称
app.include_router(app03, prefix='/chapter03', tags=['第三章 请求参数和验证'])
app.include_router(app04, prefix='/chapter04', tags=['第四章 响应处理和fastapi设置'])
app.include_router(app05, prefix='/chapter05', tags=['第五章 fastapi的依赖注入系统'])
if __name__ == '__main__':
uvicorn.run('RUN:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
#woker=1进程数量
from typing import List, Optional #Optional 参数可以为空或已经声明的类型
app = FastAPI() # 创建一个app实例
class Cityinfo(BaseModel):
province: str
country: str
is_affectse: Optional[bool] = None
@app.get("/") # 编写一个路径操作装饰器
def root(): # 编写一个路径操作函数
return {"你好!": "朋友。23"}
#@app.get("/city/{city}?q=xx") # 编写一个路径操作装饰器 q=xx 查询参数
@app.get("/city/{city}")
async def result(city: str,query: Optional[str] = None): # 编写一个路径操作函数
return {"city": city, "query": query}
if __name__ == '__main__':
uvicorn.run(app='web1:app', host="127.0.0.1", port=8000, reload=True, debug=True)
# -*- codeing = utf-8 -*-
# @time : 2021/5/6 22:55
# @file : pydantic.PY
# @Author : 夜羽
# @Software : PyCharm
#导入库时一定要注意大小写
from pydantic import BaseModel, ValidationError #可在运行时提供代码类型提示,报错是提供较为友好的错误提示
from datetime import datetime, date
from pathlib import Path #新建Path文件
from typing import List, Optional #Optional 参数可以为空或已经声明的类型
from sqlalchemy import Column, Integer, String #列,征型,字符类型
from sqlalchemy.dialects.postgresql import array #元祖
from sqlalchemy.ext.declarative import declarative_base #声明
class user(BaseModel):
id: int
name: str = "john snow" #默认值
signup_ts: Optional[datetime] = None #Optional 参数可以为空或已经声明的类型
friends: list[int] = [] #列表中元素是int类型或者是可以直接转化为int 类型的值
date = {
"id" : "123456",
"name" : 'nima',
"signup_ts" : datetime.today(),#现在的时间
"friends" :[1,'2',3]
}
user = user(**date) # **的作用是收集关键字参数到一个新的字典,并将整个字典赋值给字典_date
print(user.id,user.name) #实例化后调用属性
print(user.json()) #字典形式打开
print(user.id) #实例化后调用属性
print(user.dict()) #字典形式打开
print(user.schema())
print(user.schema_json()) #会提示使用的数据格式类型
print(user.__fields__.keys()) #查看定义的类型,所有的字段都表明类就不会乱
"""
Path = Path('pydanti.json') #新建一个jsom文件
Path.write_text('{"id" : "123456","name" : "nima","signup_ts" : datetime.today(),friends" :[1,"2",3]}')
print(user.parse_file(Path)) #解析文件 parse_解析
#写入内容 write_text写入
#现在的时间"
User_data = {'{"id" : "123","name" : "nima","signup_ts" : datetime.today(),friends" :[1,"2",3]}'}
print(user.construct(**User_data))
"""
class sound(BaseModel):
sound: str
class dog(BaseModel):
birthday: Optional[datetime] = None
weight: float = Optional[None]
sound: List[sound] # 不同的叫声. 递归模型,就是指一个嵌套一个
dogs = dog(birthday=datetime.today(), weight=6.66, sound=[{"sound": "wangwagn"}, {"sound": "yingying"}])
print(dogs.dict())
print("\033[31m5. --- ORM模型: 从类创建符合的ORM对象模型 ---\033[Om")
Base = declarative_base() #声明性基础
class companyorm(Base):
from fastapi import APIRouter, Path #APIRouter api路由器, Path 路径校验
from enum import Enum # 枚举
app03 = APIRouter()
@app03.get("/path/parametrs")
def path01():
return {"message": "this is a message"}
@app03.get("/path/{parametrs}") # 函数的顺序就是路由的顺序
def path01(parametrs: str):
return {"message": parametrs}
class cityname(str,Enum): #枚举参数类型, 使用后可以选填下方参数
beijing = "beijing China"
shanghai = "shanghai China"
@app03.get("/enum/{city}") #枚举参数类型
async def latest(city: cityname):
if city == cityname.shanghai:
return {'city_name': city, "确诊数量": 123, "死亡": 0}
if city == cityname.beijing:
return {'city_name': city, "确诊数量": 1231, "死亡": 0}
return {"city_name": city, "latest": "unknown"}
@app03.get("/files/{flie_path: path}") # 路径参数path parameters传递文件路径
def fliepath(flie_path: str):
return f"the file path is {flie_path}" #传递数据
# Path(None) 可为空 ,title="这是一个标题", description="描述", ge=1,le=10 大于1小于10
@app03.get("/path_1/{unm}")
def path03(
#num: int =Path(None, title="这是一个标题", description="描述",ge=1,le=10)
unm: int = Path(..., title="这是一个标题", description="描述", ge=1, le=10)
):
return unm
run运行文件
# -*- coding = utf-8 -*-
# @Time : 2021/5/10 10:18
# @File : RUN.PY
# @Author : 夜羽
# @Software: PyCharm
import time
from fastapi import FastAPI, Request, Request
from fastapi.middleware.cors import CORSMiddleware #跨域资源共享
from fastapi.templating import Jinja2Templates #模板渲染网页
import uvicorn #运行fastapi
from fastapi知识点教程 import app03
from fastapi知识点教程 import app04
from fastapi知识点教程 import app05
from fastapi知识点教程 import app06
from fastapi知识点教程 import app07
from fastapi知识点教程 import app08
#from coronavirus import application
from fastapi.staticfiles import StaticFiles #静态文件
#应用常见配置
app = FastAPI(
title='FastAPI 学习和 练习用的 API Docs', #标题
description='FastAPI教程 学习练习api,学习视频:https://www.bilibili.com/video/BV1iN411X72b 老师github库:https://github.com/liaogx/fastapi-tutorial', #描述
version='1.0.0', #版本号
docs_url='/docs', #路由地址
redoc_url='/redocs', #重置地址
)
app.mount(path="/static", app=StaticFiles(directory="./static"), name="static") #静态文件配置 # .mount()不要在分路由APIRouter().mount()调用,模板会报错
#拦截所有的请求 middleware中间键
@app.middleware('http')
async def add_process_time_header(request: Request, call_next): # call_next将接收request请求做为参数
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers['X-Process-Time'] = str(process_time) # 添加自定义的以“X-”开头的请求头
return response
# 跨域资源共享
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://127.0.0.1",
"http://127.0.0.1:8080"
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
#prefix= 路径 tags=[] 名称
app.include_router(app03, prefix='/chapter03', tags=['第三章 请求参数和验证'])
app.include_router(app04, prefix='/chapter04', tags=['第四章 响应处理和fastapi设置'])
app.include_router(app05, prefix='/chapter05', tags=['第五章 fastapi的依赖注入系统'])
app.include_router(app06, prefix='/chapter06', tags=['第六章 fastapi的安全、认证和授权'])
app.include_router(app07, prefix='/chapter07', tags=['第七章 fastapi的数据库操作和多应用的目录结构设计'])
app.include_router(app08, prefix='/chapter08', tags=['第八章 中间键\跨域资源共享\后台任务\测试用例'])
#app.include_router(application, prefix='/chapter07', tags=['新冠病毒疫情跟踪器'])
if __name__ == '__main__':
uvicorn.run('RUN:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
#woker=1进程数量
在一个文件中实现
@app.get("/11", tags=["渲染界面"],
responses={403: {"description": "Operation forbidden"}},)
async def root():
return {"message": "Hello World"}
@app.get("/12", tags=["渲染界面1"],
responses={403: {"description": "Operation forbidden"}},)
async def root():
return {"message": "Hello World"}
@app.get("/13", tags=["渲染界面2"],
responses={403: {"description": "Operation forbidden"}},)
async def root():
return {"message": "Hello World"}
Path(None) 可为空 ,title=“这是一个标题”, description=“描述”, ge=1,le=10 大于1小于10, alias=‘别名’ #convert_underscores=True 转换为无下划线
default=[“默认值”],
def cookie(user_agent: Optional[str] = Header(None, convert_underscores=True, description="描述"),
x_token: List[str] = Header(None, description="描述")):
unm: int = Path(..., title="这是一个标题", description="描述", ge=1, le=10)
#查询:
values: list[str] = Query(default=["v1","v2"], alias='alias_name', title="这是一个标题", description="描述",)
@app03.get("/path_1/{unm}")
def path03(
values: list[str] = Query(default=["v1","v2"], alias='alias_name', title="这是一个标题", description="描述",),
unm: int = Path(..., title="这是一个标题", description="描述", ge=1, le=10)
#num: int =Path(None, title="这是一个标题", description="描述",ge=1,le=10)
“”"
备注内容
“”"
print("\033[31m5. --- cookkie 和 header ---\033[Om")#
@app03.get("/header") #不需要使用cookie
def cookie(user_agent: Optional[str] = Header(None, convert_underscores=True, description="XIANXI"), x_token: List[str] = Header(None, description="描述")): #convert_underscores=True 转换为无下划线
"""
有些http代理和服务器是不支持在请求中带有下划线的,convert_underscores=True转换为无下划线
:param user_agent: convert_underscores=True
:param x_token: 包含多个值得列表
:return: 返还定义的属性
"""
return {"user_agent": user_agent, "x_token": x_token}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9wLZRynr-1631113988190)(C:\Users\gouguoxiong\Desktop\py后端\批注 2021-05-10.jpg)]
from enum import Enum
class cityname(str,Enum): #枚举参数类型, 使用后可以选填下方参数
beijing = "beijing China"
shanghai = "shanghai China"
#应用常见配置
app = FastAPI(
title='FastAPI Tutorial and Coronavirus Tracker API Docs', #标题
description='FastAPI教程 新冠病毒疫情跟踪器API接口文档,项目代码:https://github.com/liaogx/fastapi-tutorial', #描述
version='1.0.0', #版本号
docs_url='/docs', #路由地址
redoc_url='/redocs', #重置地址
可以写入内容
http://127.0.0.1:8000/docs
只显示不可更改
FastAPI - ReDoc
#多查询参数的列表,参数别名alias
Query
当你声明不属于路径参数
的其他函数参数时,它们将自动解释为“Query”参数,也就是查询参数。
查询参数就是一系列在URL?
之后的key-value键值对,每对键值对用 &
分割开来。例如
http://127.0.0.1:8000/items/?skip=0&limit=10
查询参数有两个,一个是skip
,一个是limit
,它们的值分别为0,10
由于它们都是URL的一部分,所以 “本质上” 它们都是字符串。
但是当你需要使用Python类型来声明query参数的时候(例如用int
),他们就会被转换为相应的类型并且依据这个类型来验证传入参数。
适用于Path参数的所有过程也适用于Query参数
from fastapi import APIRouter, Path, Query #APIRouter api路由器, Path 路径校验 Query 查询
from enum import Enum # 枚举
from typing import List, Optional #Optional 参数可以为空或已经声明的类型
print("\033[31m5. --- 查询参数, 和字符串验证 ---\033[Om")#
@app03.get("/quety")
def page_limit(page: int = 1 , limit: Optional[int] = None):
if limit:
return {"page":page, "limit": limit}
return {"page": page,}
@app03.get("/quety/bool/conversion")
def type_conversion(param: bool = False):
return param
@app03.post("/piery/validations")
def que(
value: str = Query(...,min_length=8, max_length=20, regex="^a"),
values: list[str] = Query(default=["v1","v2"], alias='alias_name')
): #多查询参数的列表,参数别名alias
return values, value
当您需要将数据从客户端(例如浏览器)发送到API时,可以将其作为 “请求体
” 发送。
请求体是客户端发送到您的API的数据。 响应体是您的API发送给客户端的数据。
API几乎总是必须发送一个响应体,但是客户端并不需要一直发送请求体。
定义请求体,需要使用 Pydantic
模型。注意以下几点
不能通过GET请求发送请求体
发送请求体数据,必须使用以下几种方法之一:POST(最常见)、PUT、DELETE、PATCH如何实现请求体
当想实现全部接口的请求时:
@app.api_route("/api", methods=("GET", "POST", "PUT"))
async def api():
return {"api": "GET, POST, PUT"}
实现请求体总共包含三个步骤。
第一步,从`pydantic`中导入`BaseModel`
```
from pydantic import BaseModel
```
第二步,创建请求体数据模型
声明请求体数据模型为一个类,且该类继承 BaseModel。所有的属性都用标准Python类。和查询参数一样:数据类型的属性如果不是必须的话,可以拥有一个默认值或者是可选None。否则,该属性就是必须的。
例如,声明了一个`JSON`对象
```python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
```
所以访问链接的时候传入的请求体可以是下面两种,
```python
{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}
```
另一种可以不传递默认值或者是可选值,(注意字典后面最后一个元素不需要逗号)
```python
{
"name": "Foo",
"price": 45.2
}
```
第三步、将模型定义为参数
将上面定义的模型添加到你的路径操作中,就和定义Path和Query参数一样的方式:
```python
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return item
```
声明参数的类型为你创建的模型 Item
这样当你使用postman选择post方法访问链接并传递一个值为
{
“name”: “Foo”,
“price”: 45.2
}
的请求体之后,会得到输出
{
“name”: “Foo”,
“price”: 45.2
}
安装postpai
使用:
print("\033[31m5. --- cookkie 和 header ---\033[Om")#
@app03.get("/cookie") #效果需要使用cookie
def cookie(cookie_id: Optional[str] = Cookie(None)): #使用cookie参数来定义cookie类
return {"coookie_id": cookie_id}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OWgQIWJX-1631113988192)(C:\Users\gouguoxiong\Desktop\py后端\批注 2021-05-10 22222.jpg)]
print("\033[31m5. --- cookkie 和 header ---\033[Om")#
@app03.get("/header") #不需要使用cookie
def cookie(user_agent: Optional[str] = Header(None, convert_underscores=True, description="XIANXI"), x_token: List[str] = Header(None, description="描述")): #convert_underscores=True 转换为无下划线
"""
有些http代理和服务器是不支持在请求中带有下划线的,convert_underscores=True转换为无下划线
:param user_agent: convert_underscores=True
:param x_token: 包含多个值得列表
:return: 返还定义的属性
"""
return {"user_agent": user_agent, "x_token": x_token}
#请求和响应的区别:返回响应不会返回密码
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header #APIRouter api路由器, Path 路径校验 Query 查询
response_model= 路径操作(参数作什么操作或者代用)
# response_model= 路径操作(参数作什么操作或者代用)
# response_model_exclude_unset=True 对前面的路径操作使用提供的值,不使用默认值
# response_model_exclude_unset=False
#response_model=Union[userout,userin] #union并集方式
#response_model=userout, #定义的类
#response_model=userout, response_model_exclude_unset=True # 对前面的路径操作使用提供的值,不使用默认值
#response_model=userin #定义的类
response_model=list[userin] #列表方式
#response_model_include=["username", "email"] #必须输出某一些字段
#response_model_exclude=["username", "email"] #必须排除
#response_model_exclude_unset=["mobile", "email"] #必须排除默认值
练习
# -*- coding = utf-8 -*-
# @Time : 2021/5/10 10:38
# @File : chapter04.PY
# @Author : 夜羽
# @Software: PyCharm
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header #APIRouter api路由器, Path 路径校验 Query 查询
app04 = APIRouter()
#响应模型
class userin(BaseModel):
username: Optional[str] = Path(None, convert_underscores=True, description="描述")
password: str
email: str
mobile: str = "电话号码"
address: str = None #地址可为空
full_name: Optional[str] = None
#请求和响应的区别:返回响应不会返回密码
class userout(BaseModel):
username: Optional[str] = Path(None, convert_underscores=True, description="描述")
email: str
mobile: str = "电话号码"
address: str = None # 地址可为空
full_name: Optional[str] = None
users ={
"user1": {"username": "user1", "password": "123456", "email": "[email protected]", "mobile": "12345678910"},
"user2": {"username": "user2", "password": "654321", "email": "[email protected]", "mobile": "12345678911"}
}
# response_model= 路径操作(参数作什么操作或者代用)
# response_model_exclude_unset=True 对前面的路径操作使用提供的值,不使用默认值 response_model_exclude_unset=False
@app04.post("/response_model", response_model=userout, response_model_exclude_unset=True)
async def res(user: userin):
print(user.password) #password 响应不会被返回
return users["user1"]
@app04.post(
"/response_model/union",
#response_model=Union[userout,userin] #union并集方式
#response_model=userout, #定义的类
#response_model=userout, response_model_exclude_unset=True # 对前面的路径操作使用提供的值,不使用默认值
#response_model=userin #定义的类
response_model=list[userin] #列表方式
#response_model_include=["username", "email"] #必须输出某一些字段
#response_model_exclude=["username", "email"] #必须排除
#response_model_exclude_unset=["mobile", "email"] #必须排除默认值
)
async def union(user: userin):
return [user, user] #列表形式两个
'''
async def union(user: userin):
return users["user1"]'''
'''
async def Union(user: userout):
return users["user2"]
'''
status 状态码
from fastapi import APIRouter, Path, Query, Cookie, Header, status #APIRouter api路由器, Path 路径校验 Query 查询 status状态码
@app04.post("/status_code", status_code=200) #status_code=200路径操作
async def status_code():
return {"status_code": 200}
@app04.post("/status_code", status_code=status.HTTP_200_OK) #status_code=200路径操作
async def status_attribute():
print(type(status.HTTP_200_OK)) #显示他的状态
return {"status_code": status.HTTP_200_OK}
Form
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCvJnado-1631113988194)(C:\Users\gouguoxiong\AppData\Roaming\Typora\typora-user-images\image-20210516205234203.png)]
#需要安装multipart :pip install python-multipart
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理
print("\033[31m5. --- Form data 表单数据处理 ---\033[Om")#
#需要安装multipart :pip install python-multipart
@app04.post("/login")
async def login(username: str = Form(...), password: str = Form(...)): #定义表单参数
return {"username": username, "password": password}
单\多文件上传及其参数详细
print("\033[31m5. --- File, UploadFile 单\多文件上传及其参数详细 ---\033[Om")#
@app04.post("/file")
#async def flie_1(flie: bytes = File(...)): #使用flie类时, 文件内容会以bytes的形式读入内存, 适合传输小文件,单个
async def file_(file: List[bytes] = File(...)): #多个文件传输
"""使用File类 文件内容会以bytes的形式读入内存 适合于上传小文件"""
return {"file_size": len(file)}
@app04.post("/upload_files")
async def upload_files(files: List[UploadFile] = File(...)): # 如果要上传单个文件 file: UploadFile = File(...)
"""
使用UploadFile类的优势:
1.文件存储在内存中,使用的内存达到阈值后,将被保存在磁盘中
2.适合于图片、视频大文件
3.可以获取上传的文件的元数据,如文件名,创建时间等
4.有文件对象的异步接口
5.上传的文件是Python文件对象,可以使用write(), read(), seek(), close()操作
"""
for file in files: #循环所有的文件
contents = await file.read() #await异步 file.read()读取
print(contents)
return {"filename": files[0].filename, "content_type": files[0].content_type} #文件的名称和类型
"""【见run.py】FastAPI项目的静态文件配置"""
"""print(user.password)"""
@app04.post(
"/path_operation_configuration", #地址
response_model=userout, #返回路由
# tags=["Path", "Operation", "Configuration"], #标签, docs界面显示接口
summary="This is summary", #接口描述
description="This is description", #描述
response_description="This is response description", #返回的结果添加描述
# deprecated=True, #废弃接口
status_code=status.HTTP_200_OK #状态码
)
async def path_operation_configuration(user: userin):
"""
Path Operation Configuration 路径操作配置
:param user: 用户信息
:return: 返回结果
"""
return users["user1"]
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException错误处理
run.py文件中
# from fastapi.exceptions import RequestValidationError
# from fastapi.responses import PlainTextResponse
# from starlette.exceptions import HTTPException as StarletteHTTPException
基本模型
raise HTTPException(
status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
@app.exception_handler(StarletteHTTPException) # 重写HTTPException异常处理器
async def http_exception_handler(request, exc):
"""
:param request: 这个参数不能省
:param exc:
:return:
"""
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
@app.exception_handler(RequestValidationError) # 重写请求验证异常处理器
async def validation_exception_handler(request, exc):
"""
:param request: 这个参数不能省
:param exc:
:return:
"""
return PlainTextResponse(str(exc), status_code=400)
代码练习
raise HTTPException
异常处理报错开头
"""Handling Errors 错误处理"""
@app04.get("/http_exception")
async def http_exception(city: str):
if city != "Beijing":
raise HTTPException(status_code=404, detail="City not found!", headers={"X-Error": "Error"}) #异常处理显示报错
return {"city": city}
@app04.get("/http_exception/{city_id}")
async def override_http_exception(city_id: int):
if city_id == 1:
raise HTTPException(status_code=418, detail="Nope! I don't like 1.")
return {"city_id": city_id}
导入依赖from fastapi import APIRouter, Path, Depends
#Depends依赖
dependencies 创建\导入和声明依赖
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException, Depends #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException错误处理 Depends依赖
#依赖的导入与申明 Depends依赖 dict = Depends(common_parameters)
from fastapi import APIRouter
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException, Depends #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException错误处理 Depends依赖
app05 = APIRouter()
print("\033[31m5. --- dependencies 创建\导入和声明依赖 ---\033[Om")#
#函数依赖
#page: int = 1,页数为1 limit: int = 100 限制为一百条
#创建依赖
async def common_parameters(q: Optional[str] = None, page:int = 1, limit:int = 100):
return {"q": q, "page": page, "limit": limit}
#依赖的导入与申明 Depends依赖 dict = Depends(common_parameters)字典形式导入依赖
@app05.get("/dependency01")
async def dependency01(commons: dict = Depends(common_parameters)):
return commons
#依赖不区分同步异步
@app05.get("/dependency02")
def dependency02(commons: dict = Depends(common_parameters)):
return commons
class as dependencies 类依赖
async def classes_as_dependencies(commons: CommonQueryParams = Depends(CommonQueryParams)): #引用依赖
async def classes_as_dependencies(commons: CommonQueryParams = Depends()): #引用依赖
async def classes_as_dependencies(commons=Depends(CommonQueryParams)): #引用依赖
print("\033[31m5. ---class as dependencies 类依赖 ---\033[Om")#
#创建一个值列表
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
#创建类依赖
class CommonQueryParams:
def __init__(self, q: Optional[str] = None, page: int = 1, limit: int = 100):
self.q = q
self.page = page
self.limit = limit
@app05.get("/classes_as_dependencies")
# async def classes_as_dependencies(commons: CommonQueryParams = Depends(CommonQueryParams)): #引用依赖
# async def classes_as_dependencies(commons: CommonQueryParams = Depends()): #引用依赖
async def classes_as_dependencies(commons=Depends(CommonQueryParams)): #引用依赖
response = {} #空字典
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.page: commons.page + commons.limit]
response.update({"items": items}) #更新到response. 字典中 ,获取数据 update更新
return response
Sub-dependencies 子依赖
use_cache=True 只调用一次依赖
print("\033[31m5. ---Sub-dependencies 子依赖 ---\033[Om")#
def query(q: Optional[str] = None):
return q
#Depends依赖
def sub_query(q: str = Depends(query), last_query: Optional[str] = None):
if not q:
return last_query
return q
#use_cache=True 只调用一次依赖
@app05.get("/sub_dependency")
async def sub_dependency(final_query: str = Depends(sub_query, use_cache=True)):
"""use_cache默认是True, 表示当多个依赖有一个共同的子依赖时,每次request请求只会调用子依赖一次,多次调用将从缓存中获取"""
return {"sub_dependency": final_query}
Dependencies in path operation decorators 路径操作装饰器中的多依赖
这时候不是在函数参数中调用依赖,而是在路径操作中 dependencies
async def verify_token(x_token: str = Header(...)):
"""没有返回值的子依赖"""
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: str = Header(...)):
"""有返回值的子依赖,但是返回值不会被调用"""
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
@app05.get("/dependency_in_path_operation", dependencies=[Depends(verify_token), Depends(verify_key)]) # 这时候不是在函数参数中调用依赖,而是在路径操作中
async def dependency_in_path_operation():
return [{"user": "user01"}, {"user": "user02"}]
主程序开启其余有可能报错
print("\033[31m5. --- Global Dependencies 全局依赖 ---\033[Om")#
#全局变量app05 = APIRouter 为开头api路由表 也可以放在主程序中调用
app05 = APIRouter(dependencies=[Depends(verify_token), Depends(verify_key)])
try: #尝试 yield db #yield 返回 finally: #最后
print("\033[31m5. --- Dependencies with yield 带yield的依赖 ---\033[Om")#
# 这个需要Python3.7才支持,Python3.6需要pip install async-exit-stack async-generator
# 以下都是伪代码
async def get_db():
db = "db_connection"
try: #尝试
yield db #yield 返回
finally: #最后
db.endswith("db_close") #假 关闭数据库
async def dependency_a():
dep_a = "generate_dep_a()"
try:
yield dep_a
finally:
dep_a.endswith("db_close")
async def dependency_b(dep_a=Depends(dependency_a)):
dep_b = "generate_dep_b()"
try:
yield dep_b
finally:
dep_b.endswith(dep_a)
async def dependency_c(dep_b=Depends(dependency_b)):
dep_c = "generate_dep_c()"
try:
yield dep_c
finally:
dep_c.endswith(dep_b)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5KuMPuGY-1631113988198)(C:\Users\gouguoxiong\AppData\Roaming\Typora\typora-user-images\image-20210519223624516.png)]
导入组件
from fastapi.security import OAuth2PasswordBearer #安全密码承载
OAuth2 密码模式和 FastAPI 的 OAuth2PasswordBearer
# -*- coding = utf-8 -*-
# @Time : 2021/5/18 23:24
# @File : chapter06.PY
# @Author : 夜羽
# @Software: PyCharm
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException, Depends #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException错误处理 Depends依赖
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm #安全密码承载, 表单中
app06 = APIRouter()
print("\033[31m5. --- OAuth2 密码模式和 FastAPI 的 OAuth2PasswordBearer ---\033[Om")#
"""
OAuth2PasswordBearer是接收URL作为参数的一个类:客户端会向该URL发送username和password参数,然后得到一个Token值
OAuth2PasswordBearer并不会创建相应的URL路径操作,只是指明客户端用来请求Token的URL地址
当请求到来的时候,FastAPI会检查请求的Authorization头信息,如果没有找到Authorization头信息,或者头信息的内容不是Bearer token,它会返回401状态码(UNAUTHORIZED)
"""
#认证方案
oauth2_schema = OAuth2PasswordBearer(tokenUrl="/chapter06/token") # 请求Token的URL地址 http://127.0.0.1:8000/chapter06/token
@app06.get("/oauth2_password_bearer")
async def oauth2_password_bearer(token: str = Depends(oauth2_schema)):
return {"token": token}
print("\033[31m5. --- 基于 Password 和 Bearer token 的 OAuth2 认证 ---\033[Om")#
fake_users_db = {
"john snow": {
"username": "john snow",
"full_name": "John Snow",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False, #判断用户是否活跃, 可以作为用户分级
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",
"hashed_password": "fakehashedsecret2",
"disabled": True, #判断用户是否活跃, 可以作为用户分级
},
}
def fake_hash_password(password: str):
return "fakehashed" + password #定义密码的加密
class User(BaseModel): #定义用户的信息格式, 返还信息
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = None
#继承上面的user , 用户值加入数据库中, 包括密码
class UserInDB(User):
hashed_password: str
@app06.post("/token") #表单中获取到用户的
async def login(form_data: OAuth2PasswordRequestForm = Depends()): #类依赖导入
user_dict = fake_users_db.get(form_data.username) #.get获取\得到
if not user_dict:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password) #或得加密密码
if not hashed_password == user.hashed_password: # 加密密码与数据库中的密码进行对比
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Incorrect username or password") #
return {"access_token": user.username, "token_type": "bearer"}
#获取用户
def get_user(db, username: str): #数据库,名称
if username in db: #如果这个名称在数据库中
user_dict = db[username]
return UserInDB(**user_dict) #python解码方式
#模拟解码token
def fake_decode_token(token: str):
user = get_user(fake_users_db, token)
return user
#获取当前的用户
async def get_current_user(token: str = Depends(oauth2_schema)):
user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, #没有经过授权
detail="Invalid authentication credentials, 非法的认证信息 ",
headers={"WWW-Authenticate": "Bearer"}, # OAuth2的规范,如果认证失败,请求头中返回“WWW-Authenticate”
)
return user
#获取当前活跃的用户
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive 未激活的用户 user")
return current_user
#获取当前活跃的用户
@app06.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException, Depends #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException错误处理 Depends依赖
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm #安全密码承载, 表单中
from passlib.context import CryptContext #密码加密
from jose import JWTError, jwt #pyhton-jose库
from datetime import datetime, timedelta
print("\033[31m5. --- OAuth2 with Password (and hashing), Bearer with JWT tokens 开发基于JSON Web Tokens的认证 ---\033[Om")#
fake_users_db.update({
"john snow": {
"username": "john snow",
"full_name": "John Snow",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
})
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" # 生成密钥 openssl rand -hex 32
ALGORITHM = "HS256" # 算法
ACCESS_TOKEN_EXPIRE_MINUTES = 10 # 访问令牌过期分钟
class Token(BaseModel):
"""返回给用户的Token"""
access_token: str
token_type: str
#对用户密码进行加密 CryptContext 密码加密 schemes=["bcrypt"]加密算法
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
#认证方案
oauth2_schema = OAuth2PasswordBearer(tokenUrl="/chapter06/jwt/token")
#对密码进行校验, 明文密码和加密后密码
def verity_password(plain_password: str, hashed_password: str):
"""对密码进行校验.verify"""
return pwd_context.verify(plain_password, hashed_password)
#获取用户
def jwt_get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
#对用户进行校验
def jwt_authenticate_user(db, username: str, password: str):
user = jwt_get_user(db=db, username=username)
if not user:
return False
if not verity_password(plain_password=password, hashed_password=user.hashed_password):
return False
return user
# #令牌过期时间
#datetime.utcnow():读取的时间一直都是系统的“世界标准时间”,不管系统的本地时区是否设置,读取的时间不会随这些设置变化;
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy() #备份一个data 后边会调用
if expires_delta: # 当前时间加上传递的时间, 主要是传递的时间
expire = datetime.utcnow() + expires_delta
else: #,如果没有传递时间就默认15分钟
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire}) #过期时间.更新到 exp(to_encode) 中
#jwt.encode编码, claims需要编码的字符, key秘钥, algorithm算法
encoded_jwt = jwt.encode(claims=to_encode, key=SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
#登录接口 response_model=路径操作
@app06.post("/jwt/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = jwt_authenticate_user(db=fake_users_db, username=form_data.username, password=form_data.password) #form表单中的用户名, 密码
if not user:
raise HTTPException(
status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
#获取到过期时间, expires_delta过期时间
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
# 获取用户
async def jwt_get_current_user(token: str = Depends(oauth2_schema)):
credentials_exception = HTTPException(
status.HTTP_401_UNAUTHORIZED,
detail="证书校验失败",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token=token, key=SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = jwt_get_user(db=fake_users_db, username=username)
if user is None:
raise credentials_exception
return user
#获取用户是否活跃
async def jwt_get_current_active_user(current_user: User = Depends(jwt_get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user")
return current_user
#获取用户是否活跃
@app06.get("/jwt/users/me")
async def jwt_read_users_me(current_user: User = Depends(jwt_get_current_active_user)):
return current_user
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = 'sqlite:///./coronavirus.sqlite3'
# SQLALCHEMY_DATABASE_URL = "postgresql://username:password@host:port/database_name" # MySQL或PostgreSQL的连接方法
engine = create_engine(
# echo=True表示引擎将用repr()函数记录所有语句及其参数列表到日志
# 由于SQLAlchemy是多线程,指定check_same_thread=False来让建立的对象任意线程都可使用。这个参数只在用SQLite数据库时设置
SQLALCHEMY_DATABASE_URL, encoding='utf-8', echo=True, connect_args={'check_same_thread': False}
)
# 在SQLAlchemy中,CRUD都是通过会话(session)进行的,所以我们必须要先创建会话,每一个SessionLocal实例就是一个数据库session
# flush()是指发送数据库语句到数据库,但数据库不一定执行写入磁盘;commit()是指提交事务,将变更保存到数据库文件
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False, expire_on_commit=True)
# 创建基本映射类
Base = declarative_base(bind=engine, name='Base')
在model文件的同目录下生成一个first名字的db文件【这个最简单,最推荐】
sqlite:///first.db
也就是app.config【SQLALCHEMY_DATABASE_URI】 = sqlite:/// + first.db
指定文件的地址,比如我想指定F盘下的pycharm_code文件夹下的flasktest文件夹下,并且名字是foo.db
那么就这样子sqlite:/// + F:\pycharm_code\flasktest\foo.db
代码上配置就可以这样子,自己可以参考一下哦~
app.config【SQLALCHEMY_DATABASE_URI】 = sqlite:/// + F:\pycharm_code\flasktest\foo.db
from sqlalchemy import Column, String, Integer, BigInteger, Date, DateTime, ForeignKey, func
from sqlalchemy.orm import relationship #关系型字段
from .database import Base
from sqlalchemy import Column, String, Integer, BigInteger, Date, DateTime, ForeignKey, func
from sqlalchemy.orm import relationship #关系型字段
from .database import Base
class City(Base):
__tablename__ = 'city' # 数据表的表名
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
province = Column(String(100), unique=True, nullable=False, comment='省/直辖市')
country = Column(String(100), nullable=False, comment='国家')
country_code = Column(String(100), nullable=False, comment='国家代码')
country_population = Column(BigInteger, nullable=False, comment='国家人口')
data = relationship('Data', back_populates='city') # 'Data'是关联的类名;back_populates来指定反向访问的属性名称
created_at = Column(DateTime, server_default=func.now(), comment='创建时间')
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment='更新时间')
__mapper_args__ = {"order_by": country_code} # 默认是正序,倒序加上.desc()方法
def __repr__(self):
return f'{self.country}_{self.province}'
class Data(Base):
__tablename__ = 'data'
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
city_id = Column(Integer, ForeignKey('city.id'), comment='所属省/直辖市') # ForeignKey里的字符串格式不是类名.属性名,而是表名.字段名
date = Column(Date, nullable=False, comment='数据日期')
confirmed = Column(BigInteger, default=0, nullable=False, comment='确诊数量')
deaths = Column(BigInteger, default=0, nullable=False, comment='死亡数量')
recovered = Column(BigInteger, default=0, nullable=False, comment='痊愈数量')
city = relationship('City', back_populates='data') # 'City'是关联的类名;back_populates来指定反向访问的属性名称
created_at = Column(DateTime, server_default=func.now(), comment='创建时间')
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment='更新时间')
__mapper_args__ = {"order_by": date.desc()} # 按日期降序排列
def __repr__(self):
return f'{repr(self.date)}:确诊{self.confirmed}例'
“”" 附上三个SQLAlchemy教程
SQLAlchemy的基本操作大全
http://www.taodudu.cc/news/show-175725.htmlPython3+SQLAlchemy+Sqlite3实现ORM教程
https://www.cnblogs.com/jiangxiaobo/p/12350561.htmlSQLAlchemy基础知识 Autoflush和Autocommit
https://zhuanlan.zhihu.com/p/48994990
“”"
首先,必须注意的是,FastAPI这个Python Web框架并没有带渲染的网页模板引擎,但是也正因为如此,它可以使用任何网页模板。
官方例子是jinjia2 。
依赖库安装
pip install jinja2 aiofiles
aiofiles是静态网页需要的包
特别说明的是,Starlette 是一个轻量级 ASGI 框架/工具包,FastAPI一大特色。
# -*- coding = utf-8 -*-
# @Time : 2021/5/26 12:37
# @File : jinja2渲染.PY
# @Author : 夜羽
# @Software: PyCharm
from pydantic import BaseModel, EmailStr #检验是否为合法的邮箱地址
from typing import Optional, List, Union #Union 并集方式
from fastapi import APIRouter, Path, Query, Cookie, Header, status, Form, File, UploadFile, HTTPException, Depends #APIRouter api路由器, Path 路径校验 Query 查询 status状态码 Form 表单数据处理 File, UploadFile上传文件 HTTPException处理 Depends依赖
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm #安全密码承载, 表单中
from passlib.context import CryptContext #密码加密
from jose import JWTError, jwt #pyhton-jose库
from datetime import datetime, timedelta
from fastapi import FastAPI, Request, Request
import uvicorn # 运行fastapi
from fastapi.templating import Jinja2Templates #模板渲染网页
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates
app = FastAPI()
app.mount(path="/static", app=StaticFiles(directory="../static"), name="static") #静态文件配置 # .mount()不要在分路由APIRouter().mount()调用,模板会报错 ,放置css.js文件
# 创建一个templates(模板)对象,以后可以重用。
templates = Jinja2Templates(directory="html")
@app.get("/items/")
async def read_item(request: Request, userid: str = Form(...)):
print('userid', userid)
return templates.TemplateResponse("item.html", {"request": request, "userid": userid})
@app.post("/user/")
async def form_text(request: Request, username: str = Form(...), password: str = Form(...), userid: str = Form(...)):
print('username', username)
print('password', password)
print('userid', userid)
# return {'text_1':text_1 , 'text_2': text_2}
return templates.TemplateResponse('item.html', {'request': request, 'username': username, 'password': password, 'userid': userid})
@app.get("/")
async def main(request: Request):
return templates.TemplateResponse('form.html', {'request': request})
if __name__ == '__main__':
uvicorn.run('jinja2渲染:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
#woker=1进程数量
tiem.html
<html>
<head>
<title>Item Detailstitle>
head>
<body>
<div class="container">
<h1>Item ID: {{ userid }}h1>
<h1>hello: {{ username }}h1>
<h1>你好: {{ password }}h1>
div>
body>
html>
form.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div class="container">
<form action="/user/" enctype="application/x-www-form-urlencoded" method="post">
<label>usernamelabel>
<br>
<input name="username" type="username" >
<br>
<label>useridlabel>
<br>
<input name="userid" type="userid" >
<br>
<label>passwordlabel>
<br>
<input name="password" type="password" >
<br><br>
<input type="submit">
form>
div>
body>
html>
大型工程应用设计:和目录拆分
# -*- coding = utf-8 -*-
# @Time : 2021/5/23 15:27
# @File : chapter07.PY
# @Author : 夜羽
# @Software: PyCharm
from fastapi import APIRouter, Depends, Request
"""【见coronavirus应用】SQL (Relational) Databases FastAPI的数据库操作"""
"""Bigger Applications - Multiple Files 多应用的目录结构设计"""
async def get_user_agent(request: Request):
print(request.headers["User-Agent"])
# app07 子应用的全局依赖
app07 = APIRouter(
prefix="/bigger_applications",
tags=["第七章 FastAPI的数据库操作和多应用的目录结构设计"], # 与run.py中的tags名称相同
dependencies=[Depends(get_user_agent)], #返回你的头标
responses={200: {"description": "Good job!"}},
)
@app07.get("/bigger_applications")
async def bigger_applications():
return {"message": "Bigger Applica
带yield的依赖的退出部分的代码 和 后台任务 会在中间件后运行
import time
from fastapi import FastAPI, Request, Request
在主程序中:
#拦截所有的请求 middleware中间键
@app.middleware('http')
async def add_process_time_header(request: Request, call_next): # call_next将接收request请求做为参数
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers['X-Process-Time'] = str(process_time) # 添加自定义的以“X-”开头的请求头
return response
跨域主要是指域名不同的
from fastapi.middleware.cors import CORSMiddleware #跨域资源共享
在主程序中:
# 跨域资源共享
app.add_middleware(
CORSMiddleware, #加载中间件
allow_origins=[ #信任的列表
"http://127.0.0.1"
"http://137.0.0.1:8080"
],
allow_creentials=True, #允许使用证书
allow_methods=['*'], #允许跨域的方法,gat\post "*"允许全部
allow_headers=['*'] #允许的请求头
)
# 跨域资源共享
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://127.0.0.1",
"http://127.0.0.1:8080"
],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
from typing import Optional
from fastapi import APIRouter, BackgroundTasks, Depends
app08 = APIRouter()
def bg_task(framework: str):
with open("README.md", mode="a") as f: #写入文件, 追加格式
f.write(f"## {framework} 框架精讲") #写入的内容
@app08.post("/background_tasks")
async def run_bg_task(framework: str, background_tasks: BackgroundTasks):
"""
:param framework: 被调用的后台任务函数的参数
:param background_tasks: FastAPI.BackgroundTasks
:return:
"""
background_tasks.add_task(bg_task, framework) #.add_task添加任务 bg_task 定义的bg任务
return {"message": "任务已在后台运行"}
#依赖注入
def continue_write_readme(background_tasks: BackgroundTasks, q: Optional[str] = None):
if q:
background_tasks.add_task(bg_task, "\n> 整体的介绍 FastAPI,快速上手开发,结合 API 交互文档逐个讲解核心模块的使用\n") #bg_task 定义的bg任务
return q
@app08.post("/dependency/background_tasks")
async def dependency_run_bg_task(q: str = Depends(continue_write_readme)):
if q:
return {"message": "README.md更新成功"}
from fastapi.testclient import TestClient
from run import app
"""Testing 测试用例"""
client = TestClient(app) # 先pip install pytest
def test_run_bg_task(): # 函数名用“test_”开头是 pytest 的规范。注意不是async def
response = client.post(url="/chapter08/background_tasks?framework=FastAPI")
assert response.status_code == 200
assert response.json() == {"message": "任务已在后台运行"}
def test_dependency_run_bg_task():
response = client.post(url="/chapter08/dependency/background_tasks")
assert response.status_code == 200
assert response.json() is None
def test_dependency_run_bg_task_q():
response = client.post(url="/chapter08/dependency/background_tasks?q=1")
assert response.status_code == 200
assert response.json() == {"message": "README.md更新成功"}
运行方式:
在终端运行cd 项目名称
+pytest
pydantic库是一种常用的用于数据接口schema定义与检查的库。
通过pydantic库,我们可以更为规范地定义和使用数据接口,这对于大型项目的开发将会更为友好。
当然,除了pydantic库之外,像是valideer库、marshmallow库、trafaret库以及cerberus库等都可以完成相似的功能,但是相较之下,pydantic库的执行效率会更加优秀一些。
因此,这里,我们仅针对pydantic库来介绍一下如何规范定义标准schema并使用。
BaseModel
类来进行定义的,所有基于pydantic的数据类型本质上都是一个BaseModel
类,它最基本的使用方式如下:from pydantic import BaseModel
class Person(BaseModel):
name: str
1.直接传值
p = Person(name="Tom")
print(p.json()) # {"name": "Tom"}
2.通过字典传入
p = {"name": "Tom"}
p = Person(**p)
print(p.json()) # {"name": "Tom"}
3.通过其他的实例化对象传入
p2 = Person.copy(p)
print(p2.json()) # {"name": "Tom"}
当传入值错误的时候,pydantic就会抛出报错,例如:
Person(person="Tom")
pydantic会抛出异常:
ValidationError: 1 validation errors for Person
name
field required (type=value_error.missing)
另一方面,如果传入值多于定义值时,BaseModel也会自动对其进行过滤。如:
p = Person(name="Tom", gender="man", age=24)
print(p.json()) # {"name": "Tom"}
可以看到,额外的参数gender与age都被自动过滤了。
通过这种方式,数据的传递将会更为安全,但是,同样的,这也要求我们在前期的schema定义中必须要尽可能地定义完全。
此外,pydantic
在数据传输时会直接进行数据类型转换,因此,如果数据传输格式错误,但是可以通过转换变换为正确的数据类型是,数据传输也可以成功,例如:
p = Person(name=123)
print(p.json()) # {"name": "123"}
下面,我们来看一下pydantic中的一些常用的基本类型。
from pydantic import BaseModel
from typing import Dict, List, Sequence, Set, Tuple
class Demo(BaseModel):
a: int # 整型
b: float # 浮点型
c: str # 字符串
d: bool # 布尔型
e: List[int] # 整型列表
f: Dict[str, int] # 字典型,key为str,value为int
g: Set[int] # 集合
h: Tuple[str, int] # 元组
# 使用 Optional[] 表示可能为 None 的值
x: Optional[str] = some_function()
具有任意类型的动态类型值
Union
联合类型
Optional
参数可以为空或已经声明的类型
Mapping
映射,是 collections.abc.Mapping 的泛型
MutableMapping
Mapping 对象的子类,可变
Generator
生成器类型, Generator[YieldType、SendType、ReturnType]
NoReturn
函数没有返回结果
Set
集合 set 的泛型, 推荐用于注解返回类型
AbstractSet
collections.abc.Set 的泛型,推荐用于注解参数
Sequence
collections.abc.Sequence 的泛型,list、tuple 等的泛化类型
TypeVar
自定义兼容特定类型的变量
NewType
声明一些具有特殊含义的类型
Callable
可调用类型, Callable[[参数类型], 返回类型]
#导入库时一定要注意大小写
from pydantic import BaseModel #可在运行时提供代码类型提示,报错是提供较为友好的错误提示
from datetime import datetime
from typing import List,Optional #Optional 参数可以为空或已经声明的类型
# -*- codeing = utf-8 -*-
# @time : 2021/5/6 22:55
# @file : pydantic.PY
# @Author : 夜羽
# @Software : PyCharm
#导入库时一定要注意大小写
from pydantic import BaseModel #可在运行时提供代码类型提示,报错是提供较为友好的错误提示
from datetime import datetime
from typing import List,Optional #Optional 参数可以为空或已经声明的类型
class user(BaseModel):
id: int
name: str = "john snow" #默认值
signup_ts: Optional[datetime] = None #Optional 参数可以为空或已经声明的类型
friends: list[int] = [] #列表中元素是int类型或者是可以直接转化为int 类型的值
date = {
"id" : "123456",
"name" : 'nima',
"friends" :[1,'2',3]
}
user = user(**date) # **的作用是收集关键字参数到一个新的字典,并将整个字典赋值给字典_date
print(user.id,user.name) #实例化后调用属性
print(user.json()) #字典形式打开
print(user.id) #实例化后调用属性
print(user.dict()) #字典形式打开
print(user.schema())
print(user.schema_json()) #会提示使用的数据格式类型
print(user.__fields__.keys()) #查看定义的类型,所有的字段都表明类就不会乱
"""
Path = Path('pydanti.json') #新建一个jsom文件
Path.write_text('{"id" : "123456","name" : "nima","signup_ts" : datetime.today(),friends" :[1,"2",3]}')
print(user.parse_file(Path)) #解析文件 parse_解析
#写入内容 write_text写入
#现在的时间"
User_data = {'{"id" : "123","name" : "nima","signup_ts" : datetime.today(),friends" :[1,"2",3]}'}
print(user.construct(**User_data))
"""
class 嵌套调用
from pydantic import BaseModel, ValidationError #可在运行时提供代码类型提示,报错是提供较为友好的错误提示
from datetime import datetime, date
from pathlib import Path #新建Path文件
from typing import List, Optional #Optional 参数可以为空或已经声明的类型
class sound(BaseModel):
sound: str
class dog(BaseModel):
birthday: Optional[datetime] = None
weight: float = Optional[None]
sound: List[sound] # 不同的叫声. 递归模型,就是指一个嵌套一个
dogs = dog(birthday=datetime.today(), weight=6.66, sound=[{"sound": "wangwagn"}, {"sound": "yingying"}])
print(dogs.dict())
SQLAlchemy - Column详解
Column常用参数:
primary_key:设置某个字段为主键。
autoincrement:设置这个字段为自动增长的。
default:设置某个字段的默认值。在发表时间这些字段上面经常用。
nullable:指定某个字段是否为空。默认值是True,就是可以为空。
unique:指定某个字段的值是否唯一。默认是False。
onupdate:在数据更新的时候会调用这个参数指定的值或者函数。在第一次插入这条数据的时候,不会用onupdate的值,只会使用default的值。常用的就是update_time(每次更新数据的时候都要更新的值)。
name:指定ORM模型中某个属性映射到表中的字段名。如果不指定,那么会使用这个属性的名字来作为字段名。如果指定了,就会使用指定的这个值作为参数。这个参数也可以当作位置参数,在第1个参数来指定。
comment:设置该字段的注释
sqlalchemy常用数据类型:
Integer:整形
Float:浮点类型
Boolean:传递True/False
DECIMAL:定点类型
enum:枚举类型
Date:传递datetime.date()进去
Time:传递datatime.time()
String:字符类型,使用时需要指定长度,区别于Text类型
Text:文本类型
LONGTEXT:长文本类型
query可以参数:
Integer:整形,映射到数据库中是int类型。
Float:浮点类型,映射到数据库中是float类型。他占据的32位。
Double:双精度浮点类型,映射到数据库中是double类型,占据64位。
String:可变字符类型,映射到数据库中是varchar类型.
Boolean:布尔类型,映射到数据库中的是tinyint类型。
DECIMAL:定点类型。是专门为了解决浮点类型精度丢失的问题的。在存储钱相关的字段的时候建议大家都使用这个数据类型。并且这个类型使用的时候需要传递两个参数,第一个参数是用来标记这个字段总能能存储多少个数字,第二个参数表示小数点后有多少位。
Enum:枚举类型。指定某个字段只能是枚举中指定的几个值,不能为其他值。在ORM模型中,使用Enum来作为枚举,示例代码如下:
聚合函数:
func.count:统计行的数量
func.avg:求平均值
func.max:求最大值
func.min:求最小值
func.sum:求和
result = session.query(func.count(User.id)).all()
print(result)
resultavg = session.query(func.avg(User.price)).all()
print(resultavg)
resultmax = session.query(func.max(User.price)).all()
print(resultmax)
resultmin = session.query(func.min(User.price)).all()
print(resultmin)
resultsum = session.query(func.sum(User.price)).all()
print(resultsum)
过滤方法:
过滤是数据 提取的一个很重要的功能,以下对一些常用的过滤条件进行详解,并且这些过滤条件都是只能通过filter方法实现的:
1.equals:
query.filter(User.name == ‘ed’)
2.not equals:
query.filter(User.name != ‘ed’)
3.like:
query.filter(User.name.like(’%ed%’))
4.in:
query.filter(User.name.in_([‘ed’,‘wendy’,‘jack’]))
#同时
query.filter(User.name.in_(session.query(User.name).filter(User.name.like(’%ed%’))))
5.not in:
query.filter(~User.name.in_(‘ed’,‘wendy’,‘jack’))
6.is null:
query.filter(User.name==None)
query.filter(User.name.is_(None))
7.is not null:
query.filter(User.name != None)
query.filter(User.name.isnot(None)
8.and:
from sqlalchemy import and_
query.filter(and_(User.name==‘ed’, User.fullname==‘Ed Jones’))
或者
query.filter(User.name==‘ed’, User.fullname==‘Ed Jones’)
或者
query.filter(User.name==‘ed’,).filter(User.fullname==‘Ed Jones’)
复制代码
9.or:
from sqlalchemy import or_
query.filter(or_(User.name=‘ed’, User.name=‘wendy’)
参考文档作者奋斗吧,青年!
文档连接:(3条消息) Python FastAPI 框架 操作 MySQL 数据库_lilygg的博客-CSDN博客
参考视频:FastAPI教程第二季(四):数据库(ORM+SQLite)(最快python异步并发web框架之一)_哔哩哔哩_bilibili
数据库表的连接
# -*- coding = utf-8 -*-
# @Time : 2021/5/31 22:49
# @File : mysqldb.PY
# @Author : 夜羽
# @Software: PyCharm
from sqlalchemy import Boolean, Column, Integer, String, DateTime
from sqlalchemy import Column, Integer, String
# 创建数据库模型(定义表结构:表名称,字段名称以及字段类型)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base #定义映射
from sqlalchemy.orm import sessionmaker #建立会话
import pymysql
from fastapi import FastAPI, Request, Request
import datetime
import uvicorn
app = FastAPI()
#app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:root@localhost:3306/school'
#DB_CONNECT = 'mysql+pymysql://root:102@localhost/tset'
#engine = create_engine('mysql+pymysql://root:root@localhost:3306/school') #用户名\密码\数据库的地址\端口号\数据库名称
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost:3306/school'
# 生成一个SQLAlchemy引擎
engine = create_engine(SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
# 生成sessionlocal类--工厂,这个类的每一个实例都是一个数据库的会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = SessionLocal()
Base = declarative_base()
# Base是用来给模型类继承的,类似django中的models.Model
#以上基本为固定
# 模型类,tablename指表名,如果数据库中没有这个表会自动创建,有表则会沿用
"""
class order_order(Base): #申明数据库某表的属性结构
__tablename__ = "order_order"
order_id = Column(String(18), primary_key=True, index=True)
state_type = Column(Integer, default=1)
submit_time = Column(DateTime)
cus_phone = Column(String(11))
cabinet_id = Column(String(4))
Base.metadata.create_all(bind=engine)
"""
class User(Base):
# 定义表名
__tablename__ = 'tbluser'
# 定义字段
# primary_key=True 设置为主键
userid = Column(Integer, primary_key=True)
username = Column(String(255))
# 构造函数
def __init__(self, userid, username):
self.userid = userid
self.username = username
# 打印形式
def __str__(self):
return "id:%s, name:%s" % (str(self.userid), self.username)
# 在数据库中生成表
Base.metadata.create_all(bind=engine)
练习
"""↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 数据库操作(依赖项) ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"""
def get_db():
try:
db = SessionLocal() # 这时,才真正产生一个'会话',并且用完要关闭
yield db # 生成器
finally:
db.close()
print('数据库已关闭')
"""↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 数据库操作方法 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"""
# 通过id查询用户信息
def get_user(db: Session, user_id: int):
CCCCCC = db.query(M_User).filter(M_User.id == user_id).first()
print(CCCCCC) # 过滤器
return CCCCCC
# 新建用户(数据库)
def db_create_user(db: Session, user: UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = M_User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit() # 提交即保存到数据库
db.refresh(db_user) # 刷新
return db_user
"""↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ post和get请求 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓"""
# 新建用户(post请求)
@app.post("/users/", response_model=User)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
# Depends(get_db)使用依赖关系可防止不同请求意外共享同一链接
return db_create_user(db=db, user=user)
# 用ID方式读取用户
@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = get_user(db, user_id=user_id)
print(db_user)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
# 定义数据模型
class CreatUser(BaseModel):
userid: int
username: str
## 添加单个
@app.post("/user/addUser")
async def InserUser(user: CreatUser):
try:
# 添加数据
dataUser = User(userid=user.userid, username=user.username)
session.add(dataUser)
session.commit()
session.close()
except ArithmeticError:
return {"code": "0002", "message": "数据库异常"}
return {"code": "0000", "message": "添加成功"}
## 添加多个
@app.post("/user/addUserList")
async def addUserList(*, user: List[CreatUser]):
try:
# user是一个列表,每个内部元素均为CreatUser类型
for u in user:
# 自定义的数据模型可以用.访问属性
dataUser = User(userid=u.userid, username=u.username)
session.add(dataUser)
session.commit()
session.close()
except ArithmeticError:
return {"code": "0002", "message": "数据库异常"}
return {"code": "0000", "message": "添加成功"}
if __name__ == '__main__':
uvicorn.run('mysqldb:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
#woker=1进程数量
code
[
{
"userid": 4,
"username": "老马"
},
{
"userid": 5,
"username": "小马"
}
]
查询数据
one与first的区别
:
one:要求结果集中只有一个结果;如果数据库返回0或2个或更多结果,并且将引发异常,则为错误。
first:返回可能更大的结果集中的第一个,如果没有结果,则返回None。不会引发异常。
filter_by与filter的区别
:
filter_by接收的参数形式是关键字参数,而filter接收的参数是更加灵活的SQL表达式结构
## 按照user_id查询
@app.get("/user/{user_id}")
async def queryUserByUserId(user_id: int):
# 创建Query查询,filter是where条件,调用one返回唯一行,调用all则是返回所有行
#filter_by接收的参数形式是关键字参数,而filter接收的参数是更加灵活的SQL表达式结构
#会以.查询(定义名称表的).where(表中.数据==输入的).结果集
try:
# user1 = session.query(User).filter_by(userid=user_id).first()
# user1 = session.query(User).filter(User.userid==user_id).one()
user1 = session.query(User).filter(User.userid==user_id).first()
session.close()
# 由于user1只有一个值,所以它直接是一个字典
if user1:
return {"code":"0000","message":"请求成功","data":user1}
else:
return {"code":"0001","message":"查询无结果"}
except ArithmeticError:
查询全部
## 查询所有
@app.get("/user/selectall/")
async def queryUserByUserId():
# 创建Query查询,filter是where条件,调用one返回唯一行,调用all则是返回所有行
try:
user1 = session.query(User).all()
session.close()
# user1 是一个列表,内部元素为字典
return {"code": "0000", "message": "请求成功", "data": user1}
except ArithmeticError:
return {"code":"0002","message":"数据库异常"}
删除命令
方法1
@app.delete("/user/deleteUser/{user_id}")
async def deleteUser(user_id: int):
try:
user1 = session.query(User).filter(User.userid == user_id).first()
if user1:
session.delete(user1)
session.commit()
session.close()
return {"code": "0000", "message": "删除成功"}
else:
return {"code": "0001", "message": "参数错误"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
方法2
本质上与方法1没区别
class AlterUser(BaseModel):
userid: int
username: str
@app.put("/user/updateUser01/")
async def deleteUser(user: AlterUser):
try:
user1 = session.query(User).filter(User.userid == user.userid).first()
if user1:
user1.username = user.username
session.commit()
session.close()
return {"code": "0000", "message": "修改成功"}
else:
return {"code": "0001", "message": "参数错误"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
# -*- coding = utf-8 -*-
# @Time : 2021/5/31 22:49
# @File : mysqldb.PY
# @Author : 夜羽
# @Software: PyCharm
from sqlalchemy import Boolean, Column, Integer, String, DateTime
from sqlalchemy import Column, Integer, String
# 创建数据库模型(定义表结构:表名称,字段名称以及字段类型)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base #定义映射
from sqlalchemy.orm import sessionmaker,Session #建立会话
import pymysql
from fastapi import FastAPI, Request, Request, Depends, HTTPException
import datetime
import uvicorn
from pydantic import BaseModel
from typing import List
app = FastAPI()
#app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:root@localhost:3306/school'
#DB_CONNECT = 'mysql+pymysql://root:102@localhost/tset'
#engine = create_engine('mysql+pymysql://root:root@localhost:3306/school') #用户名\密码\数据库的地址\端口号\数据库名称
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost:3306/school'
# 生成一个SQLAlchemy引擎
engine = create_engine(SQLALCHEMY_DATABASE_URI, pool_pre_ping=True)
# 生成sessionlocal类--工厂,这个类的每一个实例都是一个数据库的会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
session = SessionLocal()
Base = declarative_base()
# Base是用来给模型类继承的,类似django中的models.Model
# 模型类,tablename指表名,如果数据库中没有这个表会自动创建,有表则会沿用
"""
class order_order(Base): #申明数据库某表的属性结构
__tablename__ = "order_order"
order_id = Column(String(18), primary_key=True, index=True)
state_type = Column(Integer, default=1)
submit_time = Column(DateTime)
cus_phone = Column(String(11))
cabinet_id = Column(String(4))
Base.metadata.create_all(bind=engine)
"""
class User(Base):
# 定义表名
__tablename__ = 'tbluser'
# 定义字段
# primary_key=True 设置为主键
userid = Column(Integer, primary_key=True)
username = Column(String(255))
# 构造函数
def __init__(self, userid, username):
self.userid = userid
self.username = username
# 打印形式
def __str__(self):
return "id:%s, name:%s" % (str(self.userid), self.username)
# 在数据库中生成表
Base.metadata.create_all(bind=engine)
# 定义数据模型
class CreatUser(BaseModel):
userid: int
username: str
## 添加单个
@app.post("/user/addUser")
async def InserUser(user: CreatUser):
try:
# 添加数据
#数据库定义中(定义名称=赋值后.id, 赋值后.name)
dataUser = User(userid=user.userid, username=user.username)
session.add(dataUser) #添加
session.commit() #提交会提
session.close() #关闭
except ArithmeticError:
return {"code": "0002", "message": "数据库异常"}
return {"code": "0000", "message": "添加成功"}
## 添加多个
@app.post("/user/addUserList")
async def addUserList(*, user: List[CreatUser]):
try:
# user是一个列表,每个内部元素均为CreatUser类型
for u in user:
# 自定义的数据模型可以用.访问属性
dataUser = User(userid=u.userid, username=u.username)
session.add(dataUser)
session.commit()
session.close()
except ArithmeticError:
return {"code": "0002", "message": "数据库异常"}
return {"code": "0000", "message": "添加成功"}
## 按照user_id查询
@app.get("/user/{user_id}")
async def queryUserByUserId(user_id: int):
# 创建Query查询,filter是where条件,调用one返回唯一行,调用all则是返回所有行
#filter_by接收的参数形式是关键字参数,而filter接收的参数是更加灵活的SQL表达式结构
#会以.查询(定义名称表的).where(表中.数据==输入的).结果集
try:
# user1 = session.query(User).filter_by(userid=user_id).first()
# user1 = session.query(User).filter(User.userid==user_id).one()
user1 = session.query(User).filter(User.userid==user_id).first()
session.close()
# 由于user1只有一个值,所以它直接是一个字典
if user1:
return {"code": "0000", "message": "请求成功", "data": user1}
else:
return {"code":"0001","message":"查询无结果"}
except ArithmeticError:
return {"code":"0002","message":"数据库异常"}
## 查询所有
@app.get("/user/selectall/")
async def queryUserByUserId():
# 创建Query查询,filter是where条件,调用one返回唯一行,调用all则是返回所有行
try:
user1 = session.query(User).all()
session.close()
# user1 是一个列表,内部元素为字典
return {"code": "0000", "message": "请求成功", "data": user1}
except ArithmeticError:
return {"code":"0002","message":"数据库异常"}
@app.delete("/user/deleteUser/{user_id}")
async def deleteUser(user_id: int):
try:
user1 = session.query(User).filter(User.userid == user_id).first()
if user1:
session.delete(user1)
session.commit()
session.close()
return {"code": "0000", "message": "删除成功"}
else:
return {"code": "0001", "message": "参数错误"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
# 删除多个
@app.delete("/user/deleteUserList")
async def deleteUser(user_ids: List[int]):
try:
for user_id in user_ids:
user1 = session.query(User).filter(User.userid == user_id).first()
if user1:
session.delete(user1)
session.commit()
session.close()
return {"code": "0000", "message": "删除成功"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
## 根据user_id修改user_name
@app.put("/user/updateUser/")
# 定义查询参数user_id和user_name
async def deleteUser(user_id: int, user_name: str):
try:
user1 = session.query(User).filter(User.userid == user_id).first()
if user1:
user1.username = user_name
session.commit()
session.close()
return {"code": "0000", "message": "修改成功"}
else:
return {"code": "0001", "message": "参数错误"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
class AlterUser(BaseModel):
userid: int
username: str
@app.put("/user/updateUser01/")
async def deleteUser(user: AlterUser):
try:
user1 = session.query(User).filter(User.userid == user.userid).first()
if user1:
user1.username = user.username
session.commit()
session.close()
return {"code": "0000", "message": "修改成功"}
else:
return {"code": "0001", "message": "参数错误"}
except ArithmeticError:
return {"code": "0002", "message": "数据库错误"}
if __name__ == '__main__':
uvicorn.run('mysqldb:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
#woker=1进程数量
固定
方法1
:
#对象转化成python字典
def class_to_dict(obj):
is_list = obj.__class__ == [].__class__
is_set = obj.__class__ == set().__class__
if is_list or is_set:
obj_arr = []
for o in obj:
dict = {}
a = o.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
obj_arr.append(dict)
return obj_arr
else:
dict = {}
a = obj.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
return dict
方法2
:
加上了时间判断
def class_to_dict_all(obj):
"""
例如 GlobalRegion.query.all()
:param obj:
:return:
"""
is_list = obj.__class__ == [].__class__
is_set = obj.__class__ == set().__class__
if is_list or is_set:
obj_arr = []
for o in obj:
for k, v in vars(o).items(): #时间
if isinstance(v, datetime.datetime):
o.__dict__[k] = v.strftime('%Y-%m-%d %H:%M:%S')
dict = {}
a = o.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
obj_arr.append(dict)
return obj_arr
else:
dict = {}
for k, v in vars(obj).items():
if isinstance(v, datetime.datetime):
obj.__dict__[k] = v.strftime('%Y-%m-%d %H:%M:%S')
a = obj.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
return dict
输出
user1 = session.query(data).all()
print(class_to_dict(user1))
session.close()
datetime.now() 和 datetime.utcnow()
from datetime import datetime, timedelta
>>> from datetime import datetime
>>> now_time = datetime.now()
>>> utc_time = datetime.utcnow()
>>> now_time
datetime.datetime(2019, 4, 12, 14, 14, 48, 155744)
>>> utc_time
datetime.datetime(2019, 4, 12, 6, 15, 9, 989156)
utcnow()
:读取的时间一直都是系统的“世界标准时间”,不管系统的本地时区是否设置,读取的时间不会随这些设置变化;
now()
:读取的时间是系统的本地时间,也就是说,如果系统时区默认没有设置,那么读取的就是世界标准
对象转化成python字典
1.新建static文件夹下,新建images文件夹,新建图片一张
2.新建templates文件夹,新建index.html find.html login.html文件
3.新建app.py文件
index.html
<html lang="en">
<head>
head>
<body>
<a href="{{ url_for('login') }}">logina>
<br>
<a href="{{ url_for('find') }}">finda>
<img src="{{ url_for('static', filename='images/20180819002220436.png') }}">
body>
html>
123456789101112
login.html
<h1> ============= loginh1>
find.html
<h1> ============= findh1>
#-*- coding:utf-8 -*-
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/login/")
def login():
return render_template("login.html")
@app.route("/find/")
def find():
return render_template("find.html")
if __name__ == "__main__":
app.run(debug=True)
数据获得转为字典数据
#对象转化成python字典
def class_to_dict_all(obj):
"""
例如 GlobalRegion.query.all()
:param obj:
:return:
"""
is_list = obj.__class__ == [].__class__
is_set = obj.__class__ == set().__class__
if is_list or is_set:
obj_arr = []
for o in obj:
for k, v in vars(o).items():
if isinstance(v, datetime.datetime):
o.__dict__[k] = v.strftime('%Y-%m-%d %H:%M:%S')
dict = {}
a = o.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
obj_arr.append(dict)
return obj_arr
else:
dict = {}
for k, v in vars(obj).items():
if isinstance(v, datetime.datetime):
obj.__dict__[k] = v.strftime('%Y-%m-%d %H:%M:%S')
a = obj.__dict__
if "_sa_instance_state" in a:
del a['_sa_instance_state']
dict.update(a)
return dict
调用方法
try:
user1 = session.query(data).all()
user2 = class_to_dict_all(user1) #转为字典类型
session.close()
# user1 是一个列表,内部元素为字典
return templates.TemplateResponse('ui.html', {'request': request, "code": "0000", "message": "刷新成功", "data": user2})
except ArithmeticError:
return {"code":"0002","message":"数据库异常"}
图片保存测试
fastapi表单获取保存并且输出方式:
async def adduser(request: Request,
name2: str= Form(...),
xx: str = Form(...),
textfield: str = Form(...),
files_list: UploadFile = File(...)): #可以为空
start = time.time()
try:
res = await files_list.read()
# 第一个参数 文件存储路径+文件名称,存储路径目录需要提前创建好,如果没有指定,则默认会保存在本文件的同级目录下
# 第二个参数 wb,表示以二进制格式打开文件,用于只写
with open("static/file//" + files_list.filename, "wb") as f:
f.write(res)
# ../static/file/2.jpg
# src="../file/313a57f502f3429182bd8cac060aa004.png"
img = '../static/file/' + files_list.filename # 数据库图片保存地址
print(img)
# 添加数据
#数据库定义中(定义名称=赋值后.id, 赋值后.name)
dataUser = data(label=xx, user_name=name2, content=textfield, img=img)
session.add(dataUser) # 添加
session.commit() # 提交会提
session.close() # 关闭
return templates.TemplateResponse('ui.html', {'request': request, "code": "0000", "message": "添加成功" , "username1": name2, 'time': time.time() - start, 'filename': files_list.filename})
except ArithmeticError:
return templates.TemplateResponse('fail.html', {'request': request, "code": "0002", "message": "数据库异常"})
具体实现:
files_list: UploadFile = File(...)): #可以为空
start = time.time()
try:
res = await files_list.read()
# 第一个参数 文件存储路径+文件名称,存储路径目录需要提前创建好,如果没有指定,则默认会保存在本文件的同级目录下
# 第二个参数 wb,表示以二进制格式打开文件,用于只写
with open("static/file//" + files_list.filename, "wb") as f:
f.write(res)
# ../static/file/2.jpg
# src="../file/313a57f502f3429182bd8cac060aa004.png"
img = '../static/file/' + files_list.filename # 数据库图片保存地址
print(img)
# 添加数据
#数据库定义中(定义名称=赋值后.id, 赋值后.name)
dataUser = data(label=xx, user_name=name2, content=textfield, img=img)
session.add(dataUser) # 添加
session.commit() # 提交会提
session.close() # 关闭
web前端实现:
<td style="height:137px; width:148px">
<div style="height:100%;width:100%;overflow:auto">
<img alt="" src="{{ d.img }}" style="overflow:scroll;" />
div>
td>
前端
from fastapi.responses import JSONResponse, HTMLResponse, FileResponse #返回json, html, 文件
from fastapi.responses import JSONResponse, HTMLResponse, FileResponse #返回json, html, 文件
#返回一个前端文件
@app.get("/responses")
async def responses():
html_content = """
内容
"""
return HTMLResponse(content=html_content)
#返回一个图片/下载图片
@app.get("/img")
async def img():
avatar = "static/img/1.png"
return FileResponse(avatar, filename="1.png") #下载图片
#return FileResponse(avatar) #只显示预判不下载
#超链接渲染
@app.route("/12138宠物网/img")
async def index(request: Request):
return templates.TemplateResponse("index.html", {'request': request})
@app.route("/docs")
async def jm(request: Request):
return templates.TemplateResponse("index.html", {'request': request})
默认情况下,FastAPI会基于*JSONResponse
*来返回Response。
如果我们直接返回*Response
*,数据格式不会被自动转换,并且交互式文档也不会自动生成。
下面是一些常用的Response类型。
Response主类,所有其他的Response都继承自这个类。
它接收以下参数信息:
content
- str
或者 bytes
.status_code
- HTTP 状态码.headers
- 字符串字典.media_type
- media type. 例如"text/html"
.FastAPI会自动包含Content-Length,以及Content-Type,charset等头信息。
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """
Apply shampoo here.
You'll have to use soap here.
"""
return Response(content=data, media_type="application/xml")
我们可以在路径操作装饰器中声明要返回的具体*Response
类型,返回的内容会被放入到指定的Response
*中
。
并且如果我们指定的*Response
*支持JSON media类型,例如*
JSONResponse*或者*
UJSONResponse*,那么返回的数据就会被自动转换成Pydantic模型(通过*
response_model*指定)。
HTMLResponse
接收文本或者字节内容,返回HTML reponse。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
Some HTML in here
Look ma! HTML!
"""
PlainTextResponse
接收文本或者字节内容,返回纯文本 reponse。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return "Hello World"
JSONResponse
返回application/json
格式的response。FastAPI默认返回的response。
RedirectResponse
重定向返回HTTP重定向。默认状态码为307(临时重定向)。
#添加操作后直接返回前一个路径
from fastapi.responses import RedirectResponse
from fastapi.responses import RedirectResponse #重定向响应
datas = ["学习数据结构", "学习高数", "学习英语"]
@app.get("/")
def data(request: Request):
return templates.TemplateResponse("3.html", {"request": request, "data": datas})
@app.post("/data")
def add(data=Form(None)):
datas.insert(0, data)
return RedirectResponse("http://127.0.0.1:8000/", status_code=302) #重定向响应路径,需要注意前一个路径的状态码( POST请求到get请求建议使用302状态码 )
# @app.get("/typer")
# async def read_typer():
# return RedirectResponse("http://127.0.0.1:8000")
StreamingResponse
接收一个异步的发生器或者普通的发生器/枚举器,对返回结果流式输出。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
如果我们有一个file-like对象(比如用open()打开),我们就可以用*StreamingResponse
*来返回该对象。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
def main():
file_like = open(some_file_path, mode="rb"return StreamingResponse(file_like, media_type="video/mp4")
FileResponse
异步流式输出一个文件。
不同于其他response的初始化参数信息:
path
- 文件路径headers
- 定制头信息,字典格式media_type
- media type,如果没有设置则会根据文件名或文件路径来推断media typefilename
- 文件名。如果设置,会被包含到response的Content-Disposition中
文件response会包含合适的Content-Length
, Last-Modified
以及 ETag
头信息内容。
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = "large-video-file.mp4"
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(some_file_path)
xt/html"`.
FastAPI会自动包含Content-Length,以及Content-Type,charset等头信息。
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = “”"
我们可以在路径操作装饰器中声明要返回的具体*`Response`*类型,返回的内容会被放入到指定的*`Response`*`中`。
并且如果我们指定的*`Response`*`支持JSON media类型,例如*`JSONResponse`*或者*`UJSONResponse`*,那么返回的数据就会被自动转换成Pydantic模型(通过*`response_model`*指定)。`
#### `HTMLResponse`
接收文本或者字节内容,返回HTML reponse。
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return “”"
Some HTML in here
#### `PlainTextResponse`
接收文本或者字节内容,返回纯文本 reponse。
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/", response_class=PlainTextResponse)
async def main():
return “Hello World”
#### `JSONResponse`
返回`application/json`格式的response。FastAPI默认返回的response。
#### `RedirectResponse`重定向
返回HTTP重定向。默认状态码为307(临时重定向)。
#添加操作后直接返回前一个路径
from fastapi.responses import RedirectResponse
from fastapi.responses import RedirectResponse #重定向响应
datas = [“学习数据结构”, “学习高数”, “学习英语”]
@app.get("/")
def data(request: Request):
return templates.TemplateResponse(“3.html”, {“request”: request, “data”: datas})
@app.post("/data")
def add(data=Form(None)):
datas.insert(0, data)
return RedirectResponse(“http://127.0.0.1:8000/”, status_code=302) #重定向响应路径,需要注意前一个路径的状态码( POST请求到get请求建议使用302状态码 )
#### `StreamingResponse`
接收一个异步的发生器或者普通的发生器/枚举器,对返回结果流式输出。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
async def fake_video_streamer():
for i in range(10):
yield b"some fake video bytes"
@app.get("/")
async def main():
return StreamingResponse(fake_video_streamer())
如果我们有一个file-like对象(比如用open()打开),我们就可以用*`StreamingResponse`*来返回该对象。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
some_file_path = “large-video-file.mp4”
app = FastAPI()
@app.get("/")
def main():
file_like = open(some_file_path, mode="rb"return StreamingResponse(file_like, media_type=“video/mp4”)
#### `FileResponse`
异步流式输出一个文件。
不同于其他response的初始化参数信息:
- `path` - 文件路径
- `headers` - 定制头信息,字典格式
- `media_type` - media type,如果没有设置则会根据文件名或文件路径来推断media type
- `filename` - 文件名。如果设置,会被包含到response的`Content-Disposition中`
文件response会包含合适的`Content-Length`, `Last-Modified` 以及 `ETag` 头信息内容。
from fastapi import FastAPI
from fastapi.responses import FileResponse
some_file_path = “large-video-file.mp4”
app = FastAPI()
@app.get("/")
async def main():
return FileResponse(some_file_path)