FastAPI使用小结

以一个实际构建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),欢迎关注!

FastAPI使用小结

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343