山海之间

FastAPI使用小结

FastAPI使用小结
2020-04-23 · 8 min read

以一个实际构建API的例子介绍FastAPI在已有数据情况下的简单应用

简介

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

主要特征是:

  • 高速:与NodeJSGo相当,拥有高性能。 现有最快的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文件只是一个空文件,但它告诉Pythonsql_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开启验证

Pydanticorm_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),欢迎关注!

Powered by Gridea,浙ICP备17039354号-1,© 2019 - 2020🍋