FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
关键特性:
- 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。
- 高效编码:提高功能开发速度约 200% 至 300%。*
- 更少 bug:减少约 40% 的人为(开发者)导致错误。*
- 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
- 简单:设计的易于使用和学习,阅读文档的时间更短。
- 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
- 健壮:生产可用级别的代码。还有自动生成的交互式文档。
- 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
一 简单使用
- 下载FastAPI ```python pip install fastapi
pip install uvicorn
- 导入 FastAPI。
- 创建一个 app 实例。
- 编写一个**路径操作装饰器**(如 @app.get("/"))。
- 编写一个**路径操作函数**(如上面的 def root(): ...)。
- 运行开发服务器(如 uvicorn main:app --reload)。
```python
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def say_hello():
return {'code': 200, 'message': 'hello, world!'}
在开发 API 时,你通常使用特定的 HTTP 方法去执行特定的行为。
通常使用:
- POST:创建数据。
- GET:读取数据。
- PUT:更新数据。
- DELETE:删除数据。
运行:
uvicorn main:app --reload
访问 http://127.0.0.1:8000/ ,返回数据:
{"code":200,"message":"hello, world!"}
接口文档地址
二 fastapi连接MySQL
版本信息:
python 3.8.5
SQLAlchemy 1.4.27
fastapi 0.70.0
PyMySQL 1.0.2
MySQL 5.7
目录结构使用单个应用的目录结构
fastapi // 项目根目录
-venv // 虚拟环境包
-mysite // 应用目录
-- __init__.py // 将文件夹变为一个Python模块
-- database.py // 数据库配置
-- schemas.py // 数据规范格式,继承pydantic的BaseModel
-- models.py // models模型类型
-- crud.py // 对数据库的操作
-- main.py // 写接口
3.1 database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL: str = 'mysql+pymysql://root:@localhost:3306/ehsy_data?charset=utf8'
engine = create_engine(
SQLALCHEMY_DATABASE_URL, pool_pre_ping=True
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
mysql+pymysql://{用户}:{密码}@localhost:3306/{数据库}?charset=utf8
3.2 models.py
```python from sqlalchemy import Column, String,Integer
from .database import Base, engine
class ProInfo(Base): tablename = ‘pro_info_2’ # 数据库表名
id = Column(Integer, primary_key=True, index=True)
pro_url = Column(String(255), nullable=False)
order_num = Column(String(255), nullable=False)
brand_no = Column(String(255), nullable=False)
show_price = Column(String(255), nullable=False)
pro_unit = Column(String(255), nullable=False)
要与数据库中表的字段一一对应(如果数据库中已经创建了表)
<a name="Hn0dm"></a>
## 3.3 schemas.py
```python
from pydantic import BaseModel
class ProInfo(BaseModel):
id: int
pro_url: str
order_num: str
brand_no: str
show_price: str
pro_unit: str
class Config:
orm_mode = True
# 创建产品所对应的pydantic models
class ProInfoCreate(ProInfo):
pass
3.4 crud.py
from sqlalchemy.orm import Session
from . import models, schemas
# 查询前100个物料信息
def get_products(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.ProInfo).offset(skip).limit(limit).all()
def create_product(db: Session, pro: schemas.ProInfoCreate):
db_pro = models.ProInfo(id=pro.id, pro_url=pro.pro_url,
order_num=pro.order_num, brand_no=pro.brand_no, show_price=pro.show_price, pro_unit=pro.pro_unit)
db.add(db_pro)
db.commit()
db.refresh(db_pro)
return db_pro
3.5 main.py
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session
from . import crud, models, schemas
from .database import SessionLocal, engine
# 创建所有的数据库模型表
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# post方法创建产品信息
@app.post("/products/", response_model=schemas.ProInfo)
async def create_product(pro: schemas.ProInfoCreate, db: Session = Depends(get_db)):
return crud.create_product(db=db, pro=pro)
# get方法查询所有产品信息
@app.get("/products/", response_model=List[schemas.ProInfo])
async def read_products(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
products = crud.get_products(db, skip=skip, limit=limit)
return products
3.6 init.py
空
3.7 启动项目
命令启动
进入fastapi目录,使用以下命令:
uvicorn mysite.main:app --reload
mysite是应用名
用get请求访问http://127.0.0.1:8000/products/ :
用post请求(可以使用postman)访问http://127.0.0.1:8000/products/ :
注意:
- crud.py中create_product方法添加的字段要和数据库字段一一对应
- Body携带的参数名、参数个数和crud.py中定义参数名以及个数也要一一对应,否则会出错
以上成功,可以看到数据表中已经添加产品信息:
一键启动
在根目录下面新建一个**run.py**
作为项目主文件
import uvicorn
from fastapi import FastAPI
from tutorial import app03
app = FastAPI()
# prefix是请求的地址
# tags 是文档中的标题
app.include_router(app03,prefix='/chapter03',tags=['请求参数和验证'])
if __name__ == '__main__':
uvicorn.run('run:app',host='127.0.0.1',port=8000,reload=True,debug=True,workers=1)
"""
run.py -> url -> /
tutoral -> url -> /tutorial
"""
tutorial
是一个应用文件夹,里面有一个应用文件chapter03.py
**tutorial/chapter03.py**
from fastapi import APIRouter
app03 = APIRouter()
**tutorial/__init__.py**
from .chapter03 import app03
三 项目结构
是官方推荐的全栈项目生成 https://github.com/tiangolo/full-stack-fastapi-postgresql, 对我目前的需求而言,太大了;如果项目很大可以可参考项目生成的目录结构,如果是单个应用或者应用个数较少,可以参考以下目录结构 https://fastapi.tiangolo.com/zh/tutorial/bigger-applications/
3.1 单一应用目录结构
- 首先cd进入一个项目根目录文件夹中,如fastapi(最好为项目创建一个虚拟环境)
- 然后在里面创建每一个应用 ,如mysite(每一个项目中有多个应用)
每一个应用目录结构如下:
fastapi // 项目根目录
-venv // 虚拟环境包
-mysite // 应用目录
-- __init__.py // 将文件夹变为一个Python模块
-- database.py // 数据库配置
-- schemas.py // 数据规范格式,继承pydantic的BaseModel
-- models.py // models模型类型
-- crud.py // 对数据库的操作
-- main.py // 写接口
- crud.py 完成对数据库的CRUD操作
- database.py 关于数据库相关配置
- main.py fastapi路由部分
- models.py 定义表结构
- schemas.py 定义pydantic models信息,也就是schemas
3.2 多个应用目录结构(应用较少)
项目目录fastapi_project下面有两个应用student、teacher
目录结构:
fastapi_project
- venv
- student //「student」是一个「Python 子包」
-- __init__.py // 这个文件使「student」成为一个 Python 子包
-- database.py
-- schemas.py
-- models.py
-- crud.py
-- case01.py //「student」子模块,例如 import fastapi_project.student.case01
- teacher
-- __init__.py
-- database.py
-- schemas.py
-- models.py
-- crud.py
-- case02.py
- main.py //项目根目录下的main主启动文件
- 注意,case01、case02不可以是main为关键字命名,否则依赖注入时会报错
- 在student的case01.py ,配置: ```python from typing import List from fastapi import Depends, FastAPI, HTTPException from fastapi import APIRouter from sqlalchemy.orm import Session
from student import crud, models, schemas from student.database import SessionLocal,engine
student = APIRoute()
router = APIRouter(prefix=’/student’,tags=[‘学生接口’])
创建所有的数据库模型表
models.Base.metadata.create_all(bind=engine)
Dependency
def get_db(): db = SessionLocal() try: yield db finally: db.close()
“”” 以下写学生接口 “””
@router.get(“/students”, summary=”获取所有学生信息”,response_model=List[schemas.Student]) async def get_students(db:Session = Depends(get_db)): students = crud.get_students(db) return students
@router.post(‘/students’,summary = “创建学生”,response_model=schemas.Student) async def create_student(st:schemas.StudentCreate,db:Session = Depends(get_db)): return crud.create_student(db=db,st=st)
- `from fastapi import APIRouter`:导入APIRouter
- `router=APIRouter()`:创建一个APIRouter实例,在此示例中,该变量被命名为 router,但你可以根据你的想法自由命名。
- `router = APIRouter(prefix='/student',tags=['学生接口'])`:其中prefix指定此模块的路径为/student,tags备注文档信息
- `@router.get("/students", summary="获取所有学生信息",response_model=List[schemas.Student])`:使用 APIRouter 的路径操作
![image.png](https://cdn.nlark.com/yuque/0/2021/png/1283987/1637286686632-6df0464e-456b-45e6-a108-64b6f2aab5d4.png#clientId=u2d65554d-3205-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=195&id=HTrmU&margin=%5Bobject%20Object%5D&name=image.png&originHeight=260&originWidth=816&originalType=binary&ratio=1&rotation=0&showTitle=false&size=16552&status=done&style=none&taskId=ue2cb6e5f-8f7c-468f-a907-020042212c9&title=&width=612)
2. 配置项目根目录下的main主启动文件
我们将在主 FastAPI 应用中包含该 APIRouter
```python
from fastapi import FastAPI
import uvicorn
# 引入应用
from student import case01
from teacher import case02
app = FastAPI()
# 引入应用中的路由
# 直接引用应用case01里面路由-->router。访问:http://127.0.0.1:8000/students
# case01.router 包含了 fastapi_project/student/case01.py 文件中的 APIRouter。
app.include_router(case01.router)
app.include_router(case02.router)
if __name__ == '__main__':
# 启动项目
# reload:自动重载项目
# debug:调试
# workers:启动几个进程
uvicorn.run('main:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)
from student import case01
:引用项目fastapi_project下面的子包student的子模块case01.py。
注意:
我们将直接导入 student 子模块,而不是仅导入其 router 变量。
这是因为我们在 teacher 子模块中也有另一个名为 router 的变量。
如果我们一个接一个地导入,例如:
from student import router
from teacher import router
来自 teacher 的 router 将覆盖来自 student 中的 router,我们将无法同时使用它们。
因此,为了能够在同一个文件中使用它们,我们直接导入子模块:
from student import case01
app.include_router(case01.router)
:引用应用中的路由,case01.router 包含了 fastapi_project/student/case01.py 文件中的 APIRouter。
同理,应用teacher同样配置
启动项目直接运行run.py,然后访问:http://127.0.0.1:8000/student/students ,出现学生全部信息
3.3 多个应用目录结构改进
将公用的部分抽取出来
从3.2中发现每个子包下面都有database.py文件,数据库配置文件都是公共的,我们可以提取出来;
同时每一个应用如case01.py、case02.py里面都使用了依赖项(作用:每个请求有一个独立的数据库会话/连接(SessionLocal)在所有请求中使用相同的会话,然后在请求完成后关闭它。),也可以将该依赖函数提取出来
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
在fastapi_project目录下新建一个utils包,将database.py文件移入该目录下,并创建init.py
改后的目录结构:
fastapi_project
- venv
- student //「student」是一个「Python 子包」
-- __init__.py // 这个文件使「student」成为一个 Python 子包
-- schemas.py
-- models.py
-- crud.py
-- case01.py //「student」子模块,例如 import fastapi_project.student.case01
- teacher
-- __init__.py
-- schemas.py
-- models.py
-- crud.py
-- case02.py
- utils // 工具包
-- __init__.py
-- database.py //数据库配置
-- dependencies.py //依赖项
- main.py //项目根目录下的main主启动文件
改造student应用下导入database的方式:
case01.py 由原来的:
from student.database import SessionLocal,engine
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
变为:
from utils.database import SessionLocal,engine
# Dependency
from utils.dependencies import get_db
models.py 由原来的:
from .database import Base, engine
变为:
from utils.database import Base, engine
注意:
- utils包下面要新建一个init.py文件
- 导入格式不要错误,如果是
from ..utils.database import SessionLocal,engine
就会报错,正确的是:from utils.database import SessionLocal,engine
补充:
在"from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package的__init__.py文件。如果都是package,则调用顺序是YY,XX。
另外,“from . import XXX”和“from … import XXX”中的’.‘和’…’,可以等同于linux里的shell中’.‘和’…'的作用,表示当前工作目录的package和上一级的package。
至此,项目结构改造完成,核心思想:减少代码的重复,将公用部分提取出来