内容
SQLAlchemyを用いたFastAPIの実装について、公式サイトを参考に作っていきます。
関連記事
SQLAlchemyを用いたFastAPIチュートリアル パスワードハッシュ化編
SQLAlchemyを用いたFastAPIチュートリアル Basic認証編
SQLAlchemyを用いたFastAPIチュートリアル JWT認証編
※記事ができ次第リンク貼ります。
環境
Windows10
Python 3.7.6
fastapi 0.70.1
SQLAlchemy 1.4.28
環境構築
先ずは作業ディレクトリにて仮想環境を作る
>python -m venv venv
FastAPIとSQLAlchemyをインストールする
>pip install fastapi sqlalchemy
今回はwindows環境でFastAPIの動作確認をしたいため、Uvicornをインストールする
>pip install uvicorn
ファイル構成
公式サイトに倣って以下のファイル構成でFastAPIを構築する
└─ fastapi_app
├─ crud.py
├─ database.py
├─ main.py
├─ models.py
└─ schemas.py
今回はWindows環境上で動かしているため、公式サイトのコードに多少手を加えてます。
1.データベースを扱う処理を作る
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./fastapi_app.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
SQLAlchemyのcreate_engine関数でengineを作り、sessionmaker関数でengineを用いてSessionLocal変数を作っておきます。SessionLocalは後ほどmainファイルで使用します。
declarative_base関数を使ってBase変数を定義しておき、次のモデルで使用します。
2.モデルを作る
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")
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")
先ほど作成したBaseを継承してクラスを定義します。ここで定義したクラスが名データベースのテーブル名、クラス変数がカラム名、Column関数を使った定義がカラムの定義になります。
Column関数内でデータの型、primary_key、indexを設定します。
また、relationship関数を用いてUserとItemに双方向の関係を持たせています。
3.Pydanticモデルを作る
Pydanticを用いてスキーマを作ります。
Pydanticについてはこちらの記事が分かりやすかったです。
https://qiita.com/0622okakyo/items/d1dcb896621907f9002b
from typing import List, Optional
from pydantic import BaseModel
class ItemBase(BaseModel):
title: str
description: Optional[str] = None
class ItemCreate(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 User(UserBase):
id: int
is_active: bool
items: List[Item] = []
class Config:
orm_mode = True
BaseModelを継承して、ItemBase、UserBaseモデルを作り、ItemCreate/Itemクラス、UserCreate/Userクラスを定義します。
ここで定義したクラスをmain内で使用していきます。
4.CRUDを作る
CRUDとは、Create、Read、Update、Deleteの略で、データベースを操作する処理を定義しておきます。
from sqlalchemy.orm import Session
import models
import 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 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
Userを作る関数、Userを読み取る関数、Itemを作る関数、Itemを読み取る関数を定義しておきます。
パスワードは暗号化していなく平文のままですので、あくまで動作確認用としてお使いください。
5.mainアプリを作る。
最後の仕上げでmain内に今まで作成したものを反映し、APIを構築します。
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session
import crud
import models
import schemas
from database import SessionLocal, engine
import uvicorn
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Dependency
def get_db():
db = SessionLocal()
try:
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.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
if __name__ == '__main__':
uvicorn.run(app=app)
main内ではget_db関数を定義してして、databaseで定義したSessionLocalを用いてdb変数をyieldします。
yieldは少しわかりにくいですが、以下の記事が参考になります。
https://qiita.com/tomotaka_ito/items/35f3eb108f587022fa09
FastAPIのURL操作として、@app.getや@app.postを用いて、URLの定義、modelはschemaで定義したものを用います。
@app.getや@app.postの下に関数を定義し、引数は関数内で使用したい引数を付けます。URLに{user_id}と記載した場合は、引数でuser_idとして使用できます。
データベースを使用する場合は以下の引数を付けます。
db: Session = Depends(get_db)
さて、いよいよアプリを起動してみてFastAPIの動作を確認してみます。
mainの最後に以下のコードを記載してuvicornを使用して、FastAPIを立ち上げてみます。
if __name__ == '__main__':
uvicorn.run(app=app)
main.pyを実行すると、以下のようなメッセージが出ると思います。
INFO: Started server process [14692]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
http://127.0.0.1:8000/docs
docsにブラウザからアクセスして以下のページが開かれたら成功です!
FastAPIはこのdocsからGETたPOSTが簡単にできるため、非常に優れているといつも感じております。