最近需要开发一个权限系统,指定要使用
python语言,正思考这从Fask与FastAPI中选择,最后还是选择了FastAPI,由于集成了接口文档、快速(官方介绍说可以与Go、NodeJS比肩),带着尝新的心态去试试。
什么是FastAPI
FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
关键特性:
- 快速:可与
**NodeJS**和**Go**比肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。 - 高效编码:提高功能开发速度约 200% 至 300%。*
- 更少 bug:减少约 40% 的人为(开发者)导致错误。*
- 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
- 简单:设计的易于使用和学习,阅读文档的时间更短。
- 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
- 健壮:生产可用级别的代码。还有自动生成的交互式文档。
- 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
此处引用了Fast官方的介绍。
安装
FastAPI 要求 Python 3.6 及更高版本,Starlette 负责 web 部分,Pydantic 负责数据部分。
安装非常简单,只需要执行如下命令:
pip install fastapi
还会需要一个 ASGI 服务器,生产环境可以使用 Uvicorn 或者 Hypercorn。
pip install "uvicorn[standard]"
示例
创建
创建一个main.py文件,并写入如下内容:
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
运行
通过以下命令运行服务
uvicorn main:app --reload
main:main.py 文件(一个 Python "模块")。
app:在 main.py 文件中通过 app = FastAPI() 创建的对象。
--reload:让服务器在更新代码后重新启动。仅在开发时使用该选项。
交互式 API 文档
通过访问http://127.0.0.1:8000/docs,会看到自动生成的交互式 API 文档 。
项目目录规划
后续随着项目的不断开发与升级,涉及的模块也会越来越多,好的目录结构,不仅给开发提高效率而且还有更好的可读性。 以下是我最近一个项目的目录结构规划
H:.
│ .gitignore
│ Dockerfile
│ LICENSE
│ main.py #主入口文件
│ requirements.txt #依赖文件
│
├─apis
│ │ api_router.py #各模块路由文件
│ │ deps.py #数据库session依赖
│ │ __init__.py
│ │
│ └─controller #各业务模块
│ deptController.py
│ loginController.py
│ menuController.py
│ userController.py
│
├─core
│ config.py #全局配置项
│ logger.py #日志配置项
│ redis.py #redis配置项
│ __init__.py
│
├─crud #各模块数据库操作模块
│ base.py
│ sys_dept.py
│ sys_menu.py
│ sys_user.py
│ __init__.py
│
├─db
│ session.py #数据库配置项
│ __init__.py
│
├─models #数据表
│ base.py
│ sys_dept.py
│ sys_menu.py
│ sys_user.py
│ __init__.py
│
├─register #中间件
│ cors.py
│ middleware.py
│ router.py
│ __init__.py
│
├─schemas #数据校验
│ Dept.py
│ login.py
│ Menu.py
│ User.py
│ __init__.py
│
└─utils #工具模块
create_dir.py
custom_exc.py
DingTalkPushUtil.py
IpAddressTools.py
mail.py
obj_dict.py
smsUtils.py
TokenUtils.py
__init__.py
该模板了封装了一些常用的方法,例如数据库的增删改查,redis。
数据库增删改查
在该模板下的crud目录下base.py封装好了简单的增删改查,代码如下:
# -*- encoding: utf-8 -*-
'''
@File:base.py
@Author:didiplus
@Date:2022/09/23 14:02:31
@Version:1.0
@Description:封装数据库增删改查方法
'''
from unittest import result
from sqlalchemy import func, distinct, select, insert, update, desc, delete
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union
from pydantic import BaseModel
from models import Base
from sqlalchemy.ext.asyncio import AsyncSession
ModelType = TypeVar("ModelType", bound=Base)
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
class CRUDBBase(Generic[ModelType,CreateSchemaType,UpdateSchemaType]):
"""CRUD增删改查"""
def __init__(self,model:Type[ModelType]):
self.model = model
async def getById(self,db:AsyncSession,id:Any) -> Optional[ModelType]:
"""通过ID获取对象"""
sql = select(self.model).where(self.model.id == id)
result = await db.scalar(sql)
await db.close() # 释放会话
return result
async def getPage(self,db:AsyncSession,page:int = 1,size:int = 10) -> List[ModelType]:
"""分页查询"""
if page == -1 and size ==-1:
sql = select(self.model).order_by(desc(self.mode.id))
else:
sql = select(self.model).offset((page-1)*size).limit(size).order_by(desc(self.model.id))
result = await db.scalar(sql)
await db.close()
return result
async def baseSql(self,sql,db:AsyncSession):
"""抽离公共代码块"""
result = await db.execute(sql)
await db.commit()
await db.close()
return result.rowcount
async def create(self,db:AsyncSession,objIn:CreateSchemaType) -> int:
"""添加对象"""
sql = insert(self.model).values(objIn.dict())
self.baseSql(sql,db)
async def updateById(self,db:AsyncSession,id:int,objIn:Union[UpdateSchemaType,Dict[str,Any]]) -> int:
"""通过id更新对象"""
if isinstance(objIn,dict): # 判断对象是否为字典类型(更新部分字段)
obj_data = objIn
else:
obj_data = objIn.dict()
sql = update(self.model).where(self.model.id == id).values(obj_data)
self.baseSql(sql,db)
async def remove(self,db:AsyncSession,id:int) -> int:
"""通过Id删除对象"""
sql = delete(self.model).where(self.model.id ==id)
result = await db.execute(sql)
self.baseSql(sql,db)
async def batchRemove(self,db:AsyncSession,IdList:list):
"""同时删除多个对象"""
id_list = [int(item) for item in IdList ]
sql = delete(self.model).where(self.model.id.in_(id_list))
self.baseSql(sql)
该模板还有封装了其他的一些基本方法,详细的内容请移步到gitee查阅(创造不易,麻烦动手给个赞吧)