1. FastAPI:https://fastapi.tiangolo.com/zh/
  2. 慕课网:https://www.imooc.com/learn/1299

FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
关键特性:

  • 快速:可与 NodeJSGo 比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一
  • 高效编码:提高功能开发速度约 200% 至 300%。*
  • 更少 bug:减少约 40% 的人为(开发者)导致错误。*
  • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
  • 简单:设计的易于使用和学习,阅读文档的时间更短。
  • 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
  • 健壮:生产可用级别的代码。还有自动生成的交互式文档。
  • 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema

一 简单使用

  • 下载FastAPI ```python pip install fastapi

pip install uvicorn

  1. - 导入 FastAPI
  2. - 创建一个 app 实例。
  3. - 编写一个**路径操作装饰器**(如 @app.get("/"))。
  4. - 编写一个**路径操作函数**(如上面的 def root(): ...)。
  5. - 运行开发服务器(如 uvicorn main:app --reload)。
  6. ```python
  7. from fastapi import FastAPI
  8. app = FastAPI()
  9. @app.get('/')
  10. def say_hello():
  11. return {'code': 200, 'message': 'hello, world!'}

在开发 API 时,你通常使用特定的 HTTP 方法去执行特定的行为。
通常使用:

  • POST:创建数据。
  • GET:读取数据。
  • PUT:更新数据。
  • DELETE:删除数据。

运行:

  1. uvicorn main:app --reload

访问 http://127.0.0.1:8000/ ,返回数据:

  1. {"code":200,"message":"hello, world!"}

接口文档地址

http://127.0.0.1:8000/docs#/

二 fastapi连接MySQL

版本信息:

  1. python 3.8.5
  2. SQLAlchemy 1.4.27
  3. fastapi 0.70.0
  4. PyMySQL 1.0.2
  5. MySQL 5.7

目录结构使用单个应用的目录结构

  1. fastapi // 项目根目录
  2. -venv // 虚拟环境包
  3. -mysite // 应用目录
  4. -- __init__.py // 将文件夹变为一个Python模块
  5. -- database.py // 数据库配置
  6. -- schemas.py // 数据规范格式,继承pydanticBaseModel
  7. -- models.py // models模型类型
  8. -- crud.py // 对数据库的操作
  9. -- main.py // 写接口

3.1 database.py

  1. from sqlalchemy import create_engine
  2. from sqlalchemy.ext.declarative import declarative_base
  3. from sqlalchemy.orm import sessionmaker
  4. SQLALCHEMY_DATABASE_URL: str = 'mysql+pymysql://root:@localhost:3306/ehsy_data?charset=utf8'
  5. engine = create_engine(
  6. SQLALCHEMY_DATABASE_URL, pool_pre_ping=True
  7. )
  8. SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
  9. 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’ # 数据库表名

  1. id = Column(Integer, primary_key=True, index=True)
  2. pro_url = Column(String(255), nullable=False)
  3. order_num = Column(String(255), nullable=False)
  4. brand_no = Column(String(255), nullable=False)
  5. show_price = Column(String(255), nullable=False)
  6. pro_unit = Column(String(255), nullable=False)
  1. 要与数据库中表的字段一一对应(如果数据库中已经创建了表)
  2. <a name="Hn0dm"></a>
  3. ## 3.3 schemas.py
  4. ```python
  5. from pydantic import BaseModel
  6. class ProInfo(BaseModel):
  7. id: int
  8. pro_url: str
  9. order_num: str
  10. brand_no: str
  11. show_price: str
  12. pro_unit: str
  13. class Config:
  14. orm_mode = True
  15. # 创建产品所对应的pydantic models
  16. class ProInfoCreate(ProInfo):
  17. pass

3.4 crud.py

  1. from sqlalchemy.orm import Session
  2. from . import models, schemas
  3. # 查询前100个物料信息
  4. def get_products(db: Session, skip: int = 0, limit: int = 100):
  5. return db.query(models.ProInfo).offset(skip).limit(limit).all()
  6. def create_product(db: Session, pro: schemas.ProInfoCreate):
  7. db_pro = models.ProInfo(id=pro.id, pro_url=pro.pro_url,
  8. order_num=pro.order_num, brand_no=pro.brand_no, show_price=pro.show_price, pro_unit=pro.pro_unit)
  9. db.add(db_pro)
  10. db.commit()
  11. db.refresh(db_pro)
  12. return db_pro

3.5 main.py

  1. from typing import List
  2. from fastapi import Depends, FastAPI, HTTPException
  3. from sqlalchemy.orm import Session
  4. from . import crud, models, schemas
  5. from .database import SessionLocal, engine
  6. # 创建所有的数据库模型表
  7. models.Base.metadata.create_all(bind=engine)
  8. app = FastAPI()
  9. # Dependency
  10. def get_db():
  11. db = SessionLocal()
  12. try:
  13. yield db
  14. finally:
  15. db.close()
  16. # post方法创建产品信息
  17. @app.post("/products/", response_model=schemas.ProInfo)
  18. async def create_product(pro: schemas.ProInfoCreate, db: Session = Depends(get_db)):
  19. return crud.create_product(db=db, pro=pro)
  20. # get方法查询所有产品信息
  21. @app.get("/products/", response_model=List[schemas.ProInfo])
  22. async def read_products(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
  23. products = crud.get_products(db, skip=skip, limit=limit)
  24. return products

3.6 init.py

3.7 启动项目

命令启动

进入fastapi目录,使用以下命令:

  1. uvicorn mysite.main:app --reload

mysite是应用名

用get请求访问http://127.0.0.1:8000/products/
image.png

用post请求(可以使用postman)访问http://127.0.0.1:8000/products/
image.png
注意:

  • crud.py中create_product方法添加的字段要和数据库字段一一对应
  • Body携带的参数名、参数个数和crud.py中定义参数名以及个数也要一一对应,否则会出错

以上成功,可以看到数据表中已经添加产品信息:
image.png


一键启动

在根目录下面新建一个**run.py**作为项目主文件

  1. import uvicorn
  2. from fastapi import FastAPI
  3. from tutorial import app03
  4. app = FastAPI()
  5. # prefix是请求的地址
  6. # tags 是文档中的标题
  7. app.include_router(app03,prefix='/chapter03',tags=['请求参数和验证'])
  8. if __name__ == '__main__':
  9. uvicorn.run('run:app',host='127.0.0.1',port=8000,reload=True,debug=True,workers=1)
  10. """
  11. run.py -> url -> /
  12. tutoral -> url -> /tutorial
  13. """
  • tutorial是一个应用文件夹,里面有一个应用文件chapter03.py

**tutorial/chapter03.py**

  1. from fastapi import APIRouter
  2. app03 = APIRouter()

**tutorial/__init__.py**

  1. from .chapter03 import app03

目录结构:
image.png
流程:
image.png

三 项目结构

是官方推荐的全栈项目生成 https://github.com/tiangolo/full-stack-fastapi-postgresql, 对我目前的需求而言,太大了;如果项目很大可以可参考项目生成的目录结构,如果是单个应用或者应用个数较少,可以参考以下目录结构 https://fastapi.tiangolo.com/zh/tutorial/bigger-applications/

3.1 单一应用目录结构

  1. 首先cd进入一个项目根目录文件夹中,如fastapi(最好为项目创建一个虚拟环境)
  2. 然后在里面创建每一个应用 ,如mysite(每一个项目中有多个应用)

每一个应用目录结构如下:

  1. fastapi // 项目根目录
  2. -venv // 虚拟环境包
  3. -mysite // 应用目录
  4. -- __init__.py // 将文件夹变为一个Python模块
  5. -- database.py // 数据库配置
  6. -- schemas.py // 数据规范格式,继承pydanticBaseModel
  7. -- models.py // models模型类型
  8. -- crud.py // 对数据库的操作
  9. -- main.py // 写接口
  • crud.py 完成对数据库的CRUD操作
  • database.py 关于数据库相关配置
  • main.py fastapi路由部分
  • models.py 定义表结构
  • schemas.py 定义pydantic models信息,也就是schemas

image.png

3.2 多个应用目录结构(应用较少)

项目目录fastapi_project下面有两个应用studentteacher

目录结构:

  1. fastapi_project
  2. - venv
  3. - student //「student」是一个「Python 子包」
  4. -- __init__.py // 这个文件使「student」成为一个 Python 子包
  5. -- database.py
  6. -- schemas.py
  7. -- models.py
  8. -- crud.py
  9. -- case01.py //「student」子模块,例如 import fastapi_project.student.case01
  10. - teacher
  11. -- __init__.py
  12. -- database.py
  13. -- schemas.py
  14. -- models.py
  15. -- crud.py
  16. -- case02.py
  17. - main.py //项目根目录下的main主启动文件
  • 注意,case01、case02不可以是main为关键字命名,否则依赖注入时会报错
  1. 在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)

  1. - `from fastapi import APIRouter`:导入APIRouter
  2. - `router=APIRouter()`:创建一个APIRouter实例,在此示例中,该变量被命名为 router,但你可以根据你的想法自由命名。
  3. - `router = APIRouter(prefix='/student',tags=['学生接口'])`:其中prefix指定此模块的路径为/studenttags备注文档信息
  4. - `@router.get("/students", summary="获取所有学生信息",response_model=List[schemas.Student])`:使用 APIRouter 的路径操作
  5. ![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)
  6. 2. 配置项目根目录下的main主启动文件
  7. 我们将在主 FastAPI 应用中包含该 APIRouter
  8. ```python
  9. from fastapi import FastAPI
  10. import uvicorn
  11. # 引入应用
  12. from student import case01
  13. from teacher import case02
  14. app = FastAPI()
  15. # 引入应用中的路由
  16. # 直接引用应用case01里面路由-->router。访问:http://127.0.0.1:8000/students
  17. # case01.router 包含了 fastapi_project/student/case01.py 文件中的 APIRouter。
  18. app.include_router(case01.router)
  19. app.include_router(case02.router)
  20. if __name__ == '__main__':
  21. # 启动项目
  22. # reload:自动重载项目
  23. # debug:调试
  24. # workers:启动几个进程
  25. 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。

注意:

  1. 我们将直接导入 student 子模块,而不是仅导入其 router 变量。
  2. 这是因为我们在 teacher 子模块中也有另一个名为 router 的变量。
  3. 如果我们一个接一个地导入,例如:
  4. from student import router
  5. from teacher import router
  6. 来自 teacher router 将覆盖来自 student 中的 router,我们将无法同时使用它们。
  7. 因此,为了能够在同一个文件中使用它们,我们直接导入子模块:
  8. 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 ,出现学生全部信息
无标题.png

3.3 多个应用目录结构改进

将公用的部分抽取出来

从3.2中发现每个子包下面都有database.py文件,数据库配置文件都是公共的,我们可以提取出来;
同时每一个应用如case01.py、case02.py里面都使用了依赖项(作用:每个请求有一个独立的数据库会话/连接(SessionLocal)在所有请求中使用相同的会话,然后在请求完成后关闭它。),也可以将该依赖函数提取出来

  1. # Dependency
  2. def get_db():
  3. db = SessionLocal()
  4. try:
  5. yield db
  6. finally:
  7. db.close()

fastapi_project目录下新建一个utils包,将database.py文件移入该目录下,并创建init.py
改后的目录结构

  1. fastapi_project
  2. - venv
  3. - student //「student」是一个「Python 子包」
  4. -- __init__.py // 这个文件使「student」成为一个 Python 子包
  5. -- schemas.py
  6. -- models.py
  7. -- crud.py
  8. -- case01.py //「student」子模块,例如 import fastapi_project.student.case01
  9. - teacher
  10. -- __init__.py
  11. -- schemas.py
  12. -- models.py
  13. -- crud.py
  14. -- case02.py
  15. - utils // 工具包
  16. -- __init__.py
  17. -- database.py //数据库配置
  18. -- dependencies.py //依赖项
  19. - main.py //项目根目录下的main主启动文件

改造student应用下导入database的方式:

case01.py 由原来的:

  1. from student.database import SessionLocal,engine
  2. # Dependency
  3. def get_db():
  4. db = SessionLocal()
  5. try:
  6. yield db
  7. finally:
  8. db.close()

变为:

  1. from utils.database import SessionLocal,engine
  2. # Dependency
  3. from utils.dependencies import get_db

models.py 由原来的:

  1. from .database import Base, engine

变为:

  1. from utils.database import Base, engine

注意:

  1. utils包下面要新建一个init.py文件
  2. 导入格式不要错误,如果是from ..utils.database import SessionLocal,engine就会报错,正确的是:from utils.database import SessionLocal,engine

补充

  1. "from YY import XX"这样的代码中,无论是XX还是YY,只要被python解释器视作package,就会首先调用该package__init__.py文件。如果都是package,则调用顺序是YYXX
  2. 另外,“from . import XXX”和“from import XXX”中的’.‘和’…’,可以等同于linux里的shell中’.‘和’…'的作用,表示当前工作目录的package和上一级的package。

至此,项目结构改造完成,核心思想:减少代码的重复,将公用部分提取出来

示例代码

case01.py

main.py