FastAPI操作database

簡單記錄一下使用FastAPI完成對數(shù)據(jù)庫的CRUD操作。在參考文檔的基礎(chǔ)上,增加了U、D部分忌愚,全部代碼可以參考Github

1. Installation

pip install fastapi
pip install uvicorn
pip install sqlalchemy

2. File Structure

1581931091044.jpg

其中:

  • crud.py 完成對數(shù)據(jù)庫的CRUD操作
  • database.py 關(guān)于數(shù)據(jù)庫相關(guān)配置
  • main.py fastapi路由部分
  • models.py 定義表結(jié)構(gòu)
  • schemas.py 定義pydantic models信息却邓,也就是schemas
  • init.py

3. Details

3.1 database.py

首先需要定義數(shù)據(jù)庫部分菜循,作為demo,使用sqlite作為我們的數(shù)據(jù)庫申尤。這部分都是常規(guī)配置操作癌幕。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

3.2 models.py

第二步需要定義我們的表結(jié)構(gòu),這里定義了User表存儲用戶數(shù)據(jù)昧穿,item表用于存儲物品數(shù)據(jù)勺远,兩者關(guān)系是一對多。

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship

from .database import Base


class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)

    items = relationship("Item", back_populates="owner")

    def to_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}


class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)
    owner_id = Column(Integer, ForeignKey("users.id"))

    owner = relationship("User", back_populates="items")

    def to_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

3.3 schemas.py

第三步需要定義fastapi中schemas信息时鸵,后續(xù)路由胶逢、CRUD時都需要使用。由于增刪改查需要不同的schema饰潜,所以官網(wǎng)的最佳實踐一般都是通過繼承解決初坠。

from typing import List

from pydantic import BaseModel


class ItemBase(BaseModel):
    title: str
    description: str = None


class ItemCreate(ItemBase):
    pass


class ItemUpdate(ItemBase):
    pass


class Item(ItemBase):
    id: int
    owner_id: int

    class Config:
        orm_mode = True


class UserBase(BaseModel):
    email: str


class UserCreate(UserBase):
    password: str


class UserUpdate(UserBase):
    is_active: bool


class User(UserBase):
    id: int
    is_active: bool
    items: List[Item] = []

    class Config:
        orm_mode = True

3.4 crud.py

接下來就是實際完成CRUD,官網(wǎng)上沒有對update彭雾、delete做實例展示碟刺,這里補充一下。

from sqlalchemy.orm import Session

from . import models, schemas


def get_user(db: Session, user_id: int):
    return db.query(models.User).filter(models.User.id == user_id).first()


def get_user_by_email(db: Session, email: str):
    return db.query(models.User).filter(models.User.email == email).first()


def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.User).offset(skip).limit(limit).all()


def create_user(db: Session, user: schemas.UserCreate):
    fake_hashed_password = user.password + "notreallyhashed"
    db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user


def update_user(db: Session, user_id: int, update_user: schemas.UserUpdate):
    db_user = db.query(models.User).filter(models.User.id == user_id).first()
    if db_user:
        update_dict = update_user.dict(exclude_unset=True)
        for k, v in update_dict.items():
            setattr(db_user, k, v)
        db.commit()
        db.flush()
        db.refresh(db_user)
        return db_user


def delete_user(db: Session, user_id: int):
    db_user = db.query(models.User).filter(models.User.id == user_id).first()
    if db_user:
        db.delete(db_user)
        db.commit()
        db.flush()
        return db_user


def get_items(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.Item).offset(skip).limit(limit).all()


def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
    db_item = models.Item(**item.dict(), owner_id=user_id)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item


def relate_user_item(db: Session, user_id: int, item_id: int):
    db_item = db.query(models.Item).filter(models.Item.id == item_id).first()
    if db_item:
        db_item.owner_id = user_id
        db.commit()
        db.flush()
        return db.query(models.User).filter(models.User.id == user_id).first()


def update_item(db: Session, item_id: int, update_item: schemas.ItemUpdate):
    db_item = db.query(models.Item).filter(models.Item.id == item_id).first()
    if db_item:
        update_dict = update_item.dict(exclude_unset=True)
        for k, v in update_dict.items():
            setattr(db_item, k, v)
        db.commit()
        db.flush()
        db.refresh(db_item)
        return db_item


def delete_item(db: Session, item_id: int):
    db_item = db.query(models.Item).filter(models.Item.id == item_id).first()
    if db_item:
        db.delete(db_item)
        db.commit()
        db.flush()
        return db_item

3.5 main.py

最后完成路由的配置薯酝,基本都是按照官網(wǎng)上來的半沽,只是稍微補充了一些刪和改的內(nèi)容,為了讓這個demo更加完善吴菠。

from typing import List

from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from . import crud, models, schemas
from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()


# Dependency
def get_db():
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()


@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)


@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users


@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user


@app.delete('/users/{user_id}', response_model=schemas.User)
def delete_user(user_id: int, db: Session = Depends(get_db)):
    db_user = crud.delete_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user


@app.put("/users/{user_id}", response_model=schemas.User)
def update_user(user_id: int, update_user: schemas.UserUpdate, db: Session = Depends(get_db)):
    updated_user = crud.update_user(db, user_id, update_user)
    if updated_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return updated_user


@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
    user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
    return crud.create_user_item(db=db, item=item, user_id=user_id)


@app.get("/items/", response_model=List[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    items = crud.get_items(db, skip=skip, limit=limit)
    return items


@app.put("/items/{user_id}/{item_id}/", response_model=schemas.User)
def relate_user_item(user_id: int, item_id: int, db: Session = Depends(get_db)):
    user = crud.relate_user_item(db=db, item_id=item_id, user_id=user_id)
    return user


@app.put("/items/{item_id}", response_model=schemas.Item)
def update_item(item_id: int, update_item: schemas.ItemUpdate, db: Session = Depends(get_db)):
    updated_item = crud.update_item(db, item_id, update_item)
    if updated_item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return updated_item


@app.delete('/items/{item_id}', response_model=schemas.Item)
def delete_item(item_id: int, db: Session = Depends(get_db)):
    db_item = crud.delete_item(db, item_id=item_id)
    if db_item is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_item

4. Tests

在文件路徑下執(zhí)行者填,--reload當(dāng)代碼有修改時,可以自動加載做葵。

uvicorn sql_app.main:app --reload

可以通過postman發(fā)送請求做測試占哟,這里貼圖一張。


1581943691192.jpg

最后fastapi還可以自動生成文檔,可以訪問http://127.0.0.1:8000/docs查閱榨乎。

1581943651716.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗓化,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谬哀,更是在濱河造成了極大的恐慌刺覆,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件史煎,死亡現(xiàn)場離奇詭異谦屑,居然都是意外死亡,警方通過查閱死者的電腦和手機篇梭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門氢橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恬偷,你說我怎么就攤上這事悍手。” “怎么了袍患?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵坦康,是天一觀的道長。 經(jīng)常有香客問我诡延,道長滞欠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任肆良,我火速辦了婚禮筛璧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惹恃。我一直安慰自己夭谤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布巫糙。 她就那樣靜靜地躺著朗儒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曲秉。 梳的紋絲不亂的頭發(fā)上采蚀,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天疲牵,我揣著相機與錄音承二,去河邊找鬼。 笑死纲爸,一個胖子當(dāng)著我的面吹牛亥鸠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼负蚊,長吁一口氣:“原來是場噩夢啊……” “哼神妹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起家妆,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鸵荠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伤极,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛹找,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年哨坪,在試婚紗的時候發(fā)現(xiàn)自己被綠了庸疾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡当编,死狀恐怖届慈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忿偷,我是刑警寧澤金顿,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站鲤桥,受9級特大地震影響串绩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芜壁,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一礁凡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慧妄,春花似錦顷牌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饱普,卻和暖如春运挫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背套耕。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工谁帕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冯袍。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓匈挖,卻偏偏與公主長得像碾牌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子儡循,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. DJANGO使用指南 Django簡介: Django官網(wǎng)地址[https://www.djangoproj...
    文化銀兒閱讀 720評論 0 1
  • 官網(wǎng)網(wǎng)站:https://www.djangoproject.com/官網(wǎng)文檔:https://docs.djan...
    sunshaoping閱讀 2,153評論 0 1
  • 一舶吗、未央?yún)^(qū) 1: 漢神四層的荊山塬,餃子择膝,子卷誓琼,快餐米飯,葫蘆頭肴捉,小抄踊赠,面條,饸烙每庆,燴菜筐带,羊血粉湯,豆腐腦缤灵,...
    朝九晚六的上班族閱讀 572評論 1 1
  • 那天你說伦籍,好想有錢,如果有錢腮出,和父母的問題就沒有了帖鸦。 細(xì)思極恐。 家應(yīng)該是我們好和壞胚嘲,窮和富最終都可以回的地方作儿。不...
    純子404閱讀 174評論 0 0
  • 周六陽光明媚 1、感恩自己創(chuàng)造的神奇的一天 感恩今天睡了個懶覺 感恩自己洗衣服 感恩姨媽的折磨馋劈,從來沒有這么痛過攻锰。...
    jean_c330閱讀 156評論 0 0