以一个实际构建API
的例子介绍FastAPI
在已有数据情况下的简单应用
简介
FastAPI
是一个现代、快速(高性能)的 Web
框架,基于标准 Python
类型提示,使用 Python 3.6+
构建 API
。
主要特征是:
- 高速:与
NodeJS
和Go
相当,拥有高性能。 现有最快的Python
框架之一。 - 快速编码:将功能开发速度提高约200%至300%。
- 更少的
Bug
:减少约40%的人为(开发人员)导致的错误。 - 直观:更好的编辑支持。补全任何地方。更少的调试时间。
- 简单:方便使用和学习。减少阅读文档的时间。
- 简介:最小化代码重复。每个参数声明的多个要素。更少的错误。
- 健壮:获取便于生产的代码。带自动交互式文档。
- 基于标准:基于(并完全兼容)
API
的开放标准:OpenAPI
(以前称为Swagger
)和JSON Schema
。
文档:https://fastapi.tiangolo.com
源码:https://github.com/tiangolo/fastapi
需求及依赖
在Mysql
中数据库中有一个Gene
表,开发接口实现对这个表的数据的增删改查。
Python
版本需要3.7+
,依赖包如下:
certifi == 2020.4.5.1
click == 7.1.1
fastapi == 0.54.1
h11 == 0.9.0
importlib-metadata == 1.6.0
inflect == 4.1.0
pydantic == 1.4
PyMySQL == 0.9.3
sqlacodegen == 2.1.0
SQLAlchemy == 1.3.16
starlette == 0.13.2
uvicorn == 0.11.3
websockets == 8.1
wincertstore == 0.2
zipp == 3.1.0
FastAPI
是一个轻量级的框架,与数据库的通信是通过SQLAlchemy
包来实现的。
文件结构
以sql_app
为项目名,简单的文件结构如下:
.
└── sql_app
├── __init__.py
├── crud.py
├── database.py
├── main.py
├── models.py
└── schemas.py
__init__.py
文件只是一个空文件,但它告诉Python
把sql_app
作为一个模块来使用。下面对每个文件进行说明。
数据库连接
在sql_app/database.py
文件中,创建与数据库的连接,整体代码如下:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://user:password@127.0.0.1:3306/cloud" #使用pymysql作为驱动,cloud是数据库名称
engine = create_engine(
SQLALCHEMY_DATABASE_URL
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base() #返回一个类,后续作为数据库模型的基类(ORM模型)
创建模型
sql_app/models.py
文件中是数据库的对应SQLAlchemy
模型,如果数据库已经数据存在。可以使用sqlacodegen
直接生成每个表的model
:
sqlacodegen --outfile=models.py mysql://user:password@127.0.0.1:3306/cloud
如果不指定outfile
,则会直接在屏幕上输出。
from sqlalchemy import CHAR, Column, DateTime, ForeignKey, Index, String
from sqlalchemy.dialects.mysql import INTEGER, LONGTEXT, SMALLINT, TINYINT
from sqlalchemy.orm import relationship
from .database import Base
class Gene(Base):
__tablename__ = 'Gene'
id = Column(INTEGER(11), primary_key=True)
gene = Column(String(50, 'utf8mb4_unicode_ci'), nullable=False)
summary = Column(LONGTEXT)
publish_time = Column(DateTime, nullable=False)
edit_time = Column(DateTime, nullable=False)
content = Column(LONGTEXT)
status = Column(SMALLINT(6), nullable=False)
如果有外键依赖,sqlacodegen
也可以完美的生成,无需手动。对于那些字段多的表,而且还有N
个需要弄的表,sqlacodegen
可以极大的提升效率。
创建Pydantic模型
Pydantic
可以基于Python
的类型提示来进行数据验证。我们使用schmas.py
来保存Pydantic
模型,以便和保存SQLAlchemy
模型的models.py
文件进行区分。
from datetime import datetime
from pydantic import BaseModel
class GeneBase(BaseModel):
gene: str
class GeneCreate(GeneBase):
summary: str
publish_time: datetime
edit_time: datetime
content: str
status: int
class Gene(GeneBase):
id: int
summary: str
publish_time: datetime
edit_time: datetime
status: int
class Config:
orm_mode = True # 为Pydantic开启验证
Pydantic
的orm_model
将告诉Pydantic
模型读取数据,它不仅字典,还是ORM
模型(或具有属性的任何其他任意对象)。因此,Pydantic
模型与ORM
兼容,我们可以在接口路径操作中的response_model
参数中声明它。
CRUD
接着就是数据库的增删改查操作
from sqlalchemy.orm import Session
from . import models, schemas
def get_gene(db: Session, gene_id: int):
return db.query(models.Gene).filter(models.Gene.id == gene_id).first()
def get_gene_by_name(db: Session, gene_name: str):
return db.query(models.Gene).filter(models.Gene.gene == gene_name).first()
def get_genes(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Gene).offset(skip).limit(limit).all()
def create_gene(db: Session, gene: schemas.GeneCreate):
db_gene = models.Gene(gene=gene.gene, summary=gene.summary,publish_time=gene.publish_time,edit_time=gene.edit_time, content=gene.content,auditor_id=gene.auditor_id,author_id=gene.author_id, status=gene.status)
db.add(db_gene)
db.commit()
db.refresh(db_gene)
return db_gene
def update_gene(db: Session, gene: schemas.GeneCreate):
db.query(models.Gene).filter(models.Gene.gene == gene.gene).update(gene)
db.commit()
return db.query(models.Gene).filter(models.Gene.gene == gene.gene).first()
def delete_gene(db: Session, gene_id: int):
db.query(models.Gene).filter(models.Gene.id == gene_id).delete()
db.commit()
return {'Result': '删除成功'}
API
最后就是需要实现的API
接口了,放在main.py
里面。
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session
from . import crud, schemas
from .database import SessionLocal
app = FastAPI(
title="Gene接口项目",
description="通过接口实现Gene表的增删改查",
version="0.1.0",
openapi_url="/api/v1/api.json",
)
def get_db():
db = ''
try:
db = SessionLocal()
yield db
finally:
db.close()
@app.post("/genes/", response_model=schemas.Gene, summary='新增基因表', description='json格式以post方式提交')
def create_gene(gene: schemas.GeneCreate, db: Session = Depends(get_db)):
db_gene = crud.get_gene_by_name(db, gene_name=gene.gene)
if db_gene:
raise HTTPException(status_code=400, detail=f"{gene.gene}基因已存在")
return crud.create_gene(db=db, gene=gene)
@app.post('/genes/delete/{gene_id}', summary='删除基因信息', description='根据基因ID删除基因信息')
def del_gene(gene_id: int, db: Session = Depends(get_db)):
db_user = crud.get_gene(db, gene_id)
if db_user is None:
raise HTTPException(status_code=404, detail="基因id错误或id不存在")
return crud.delete_gene(db, gene_id)
@app.post("/genes/update", response_model=schemas.Gene, summary='更新已有基因表', description='json格式以post方式提交')
def update_gene(gene: schemas.GeneCreate, db: Session = Depends(get_db)):
db_gene = crud.get_gene_by_name(db, gene_name=gene.gene)
if db_gene is None:
raise HTTPException(status_code=400, detail=f"{gene.gene}基因不存在")
return crud.update_gene(db=db, gene=gene)
@app.get("/genes/", response_model=List[schemas.Gene], summary='获取基因列表', description='/genes/?skip=0&limit=5, 无参数返回所有结果')
def read_users(skip: int = 0, limit: int = 5, db: Session = Depends(get_db)):
users = crud.get_genes(db, skip=skip, limit=limit)
return users
@app.get("/genes/{gene_id}", response_model=schemas.Gene, summary='根据基因id获取基因信息', description='如/genes/1')
def read_user(gene_id: int, db: Session = Depends(get_db)):
db_gene = crud.get_gene(db, gene_id=gene_id)
if db_gene is None:
raise HTTPException(status_code=404, detail="基因id错误或id不存在")
return db_gene
@app.get("/genes/{gene}", response_model=schemas.Gene, summary='根据基因名称获取基因信息', description='如/genes/EGFR')
def read_user(gene: str, db: Session = Depends(get_db)):
db_user = crud.get_gene_by_name(db, gene_name=gene)
if db_user is None:
raise HTTPException(status_code=404, detail="基因名称错误或基因不存在")
return db_user
通过装饰器实现路由和参数传递方法
启动
使用uvicorn
启动
uvicorn sql_app.main:app --reload
本文首发于公众号:柠檬培养师(ID: yantinger90),欢迎关注!