はじめに
今回PythonのライブラリStreamlit
とWebフレームワークFastAPI
を使って簡単なメモアプリを作ってみました。即席でWebアプリケーションを作るのに有用かつ、お手軽に作成できたので紹介させてください。
目標
メモ登録・一覧画面をStreamlit
、FastAPI
を使用して作成していきます。
イメージとして下記2画面です。
メモ登録画面
メモ一覧画面
Streamlit
Streamlitはデータサイエンス領域などで WebUI 付きのアプリケーションを手早く作るためのソフトウェアです。こちらを使ってフロントエンドのアプリケーションを作成していきます。
FastAPI
FastAPIは、Pythonの標準である型ヒントに基づいてPython 3.6 以降でAPI を構築するための、モダンで高速なWeb フレームワークです。
- 高速: NodeJS や Go 並みのとても高いパフォーマンス (Starlette と pydantic のおかげです)。 最も高速な Python フレームワークの一つです.
- 高速なコーディング: 開発速度を約 200%~300%向上させます。
- 少ないバグ: 開発者起因のヒューマンエラーを約 40%削減します。
- 直感的: 素晴らしいエディタのサポートや デバッグ時間を削減します。
- 簡単: 簡単に利用、習得できるようにデザインされています。ドキュメントを読む時間を削減します。
- 短い: コードの重複を最小限にしています。各パラメータからの複数の機能。少ないバグ。
- 堅牢性: 自動対話ドキュメントを使用して、本番環境で使用できるコードを取得します。
- Standards-based: API のオープンスタンダードに基づいており、完全に互換性があります: OpenAPI (以前は Swagger として知られていました) , JSON スキーマ
Flask
より使い勝手が良くて、Django
よりも学習コストが少ない、ドキュメントを自動で作ってくれる良さげなイメージですね。
環境構築
使用したバージョン
- Python・・・
3.9.7
- Streamlit・・・
1.4.0
- FastAPI・・・
0.71.0
ライブラリインストール
まずはPythonがインストールされている環境でライブラリをインストールしていきましょう。FastAPI
の実行環境として ASGI サーバーも必要になりますので、uvicornも併せてインストールします。
pip install streamlit fastapi uvicorn
Streamlit動作確認
front.py
に下記コードを記載していきましょう。
import streamlit as st
st.title('Hello World')
Hello World
を表示するのに必要なコードはこれだけです。コマンドを実行してサーバーを立ち上げてみましょう。
streamlit run front.py
FastAPI動作確認
main.py
に下記コードを記載します。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def get():
return 'Hello World'
コードが書けたらこちらも実行してみましょう。
uvicorn main:app --reload
Hello World
が出力されたらOKです!
どちらとも動作確認出来たら本格的に実装を進めていきます。
ディレクトリ構成
.
├── main.py Streamlitメイン関数
├── memo.db メモデータ用DB
└── memoback
├── __init__.py
├── crud.py CRUD処理を記載
├── database.py DB接続処理を記載
├── main.py バックエンドメイン関数
├── model.py DBモデル定義
└── schema.py 型情報
バックエンド実装
まずはバックエンドから実装を進めていきます。
DB準備
今回はお手軽さ重視で、DBはSQLite
、O/RマッパーはSQLAlchemy
を採用して進めていきます。必要なモジュールをインストールします。
pip install sqlalchemy
モジュールをインストールしたらDBの設定をdatabase.py
に記載していきます。
from sqlalchemy import create_engine, engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = 'sqlite:///./memo.db'
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={'check_same_thread': False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
モデル定義
メモアプリに必要なDB定義をmodel.py
に記載していきます。
必要最小限のカラムとして、主キーにmemo_id
、内容のcontent
だけテーブルに持ちます。
from sqlalchemy import Column, Integer, String
from .database import Base
class Memo(Base):
__tablename__ = 'memos'
memo_id = Column(Integer, primary_key=True, index=True, autoincrement=True)
content = Column(String, index=True)
pydanticによる型定義
pydantic
はPython の型アノテーションを利用して、実行時における型ヒントを提供したり、データ検証時のエラー設定を簡単に提供してくれるためのライブラリです。(FastAPI
インストール時に同時にインストールされています)
このpydantic
を活用して、メモ参照時と登録時の定義を下記のようにschema.py
に記載していきます。
from pydantic import BaseModel, Field
# メモ参照定義
class MemoSchema (BaseModel):
memo_id: int = Field()
content: str = Field(max_length=100)
class Config:
orm_mode = True
# メモ登録定義
class MemoCreatingSchema (BaseModel):
content: str = Field(max_length=100)
class Config:
orm_mode = True
DB参照・登録
次に実際にmemos
テーブルに参照・登録するコードをcrud.py
に記載していきます。
from sqlalchemy.orm import Session
from . import model, schema
# メモ一覧取得
def get_memos(db: Session, skip: int = 0, limit: int = 100):
return db.query(model.Memo).offset(skip).limit(limit).all()
# メモ登録
def create_memo(db: Session, memo: schema.MemoCreatingSchema):
db_memo = model.Memo(content = memo.content)
db.add(db_memo)
db.commit()
db.refresh(db_memo)
return db_memo
関数create_memo
の第2引数ではpydantic
で定義した型を指定することで、リクエストで渡ってきたmemoテーブルのmodelが適切かどうか検証することができます。
誤ったリクエストを送った場合はStatusCode422(Unprocessable Entity)
が返却され、不備内容についても下記のような詳細が返却されます。
{
"detail": [
{
"loc": [
"body",
"content"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
ルーティング
最後にルーティング処理をmain.py
に記載してバックエンドの実装は完了です。
from typing import List
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from . import model, schema, crud
from .database import SessionLocal, engine
model.Base.metadata.create_all(bind=engine)
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# メモ参照
@app.get("/memos", response_model=List[schema.MemoSchema])
async def read_memos(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
memos = crud.get_memos(db, skip=skip, limit=limit)
return memos
#メモ登録
@app.post("/memos", response_model=schema.MemoSchema)
async def create_memo(memo: schema.MemoCreatingSchema, db: Session = Depends(get_db)):
return crud.create_memo(db=db, memo=memo)
ここで注目なのが、@app.get,@app.post
デコレータの第2引数です。pydantic
を使った型情報を引数で渡すことでデータ検証が実行され、定義にそぐわないレスポンスを実装しようとした場合にエラーで気づくことができます。また自動ドキュメント作成、JSONレスポンスへの変換などにも第2引数の型情報が使われています。
第2引数を指定せずレスポンスを返す方法も公式ドキュメントに記載があるので参照いただければと思います。
自動ドキュメント作成・動作確認
これでバックエンドの実装は完了です!試しにサーバーを立ち上げて動作確認してみましょう!
FastAPI
の機能である自動ドキュメント作成について紹介していきます。サーバー立ち上げ後に下記URLにアクセスすると自動でドキュメントが生成されたページを確認できます。
http://{host名}/docs
Swagger UI
が表示され、実際にリクエストを送ってテストすることもできます。
今回は紹介だけに留めてメモ参照・登録のリクエストを実行していきます。
メモ登録(POST:http://127.0.0.1:8000/memos
)
{
"content": "testです"
}
{
"memo_id": 12,
"content": "testです"
}
メモ参照(GET:http://127.0.0.1:8000/memos
)
[
{
"memo_id": 1,
"content": "テストです"
},
{
"memo_id": 2,
"content": "テストです"
},
{
"memo_id": 3,
"content": "テストです"
},
{
"memo_id": 4,
"content": "メモを登録してみます!!"
},
{
"memo_id": 5,
"content": "次のメモを確認"
},
{
"memo_id": 6,
"content": "メモを登録してみます!!"
},
{
"memo_id": 12,
"content": "testです"
},
]
フロントエンド実装
main.py
にフロントエンドの処理を記載していきます。今回はメモ登録・参照できる2画面を作成していきます。画面の切り替えはStreamlit
の機能sidebar
を使って実装していきます。
また、メモ登録結果のレスポンスを表示するために、st.json(res.json())
で確認できるようにします。
import streamlit as st
import requests
import json
page = st.sidebar.selectbox('Choose your page', ['registration', 'list'])
if page == 'registration':
st.title('メモ登録画面')
with st.form(key='registration'):
content: str = st.text_input('メモ内容', max_chars=100)
data = {
'content': content
}
submit_button = st.form_submit_button(label='メモ登録')
if submit_button:
url = 'http://127.0.0.1:8000/memos'
res = requests.post(
url,
data=json.dumps(data)
)
if res.status_code == 200:
st.success('メモ登録完了')
st.json(res.json())
elif page == 'list':
st.title('メモ一覧画面')
res = requests.get('http://127.0.0.1:8000/memos')
records = res.json()
for record in records:
st.subheader('・' + record.get('content'))
動作確認
メモ登録・参照してみましょう!
メモ登録画面
成功した場合、レスポンスを画面上で確認できます。
メモ一覧画面
メモ一覧および先ほど登録したメモを確認出来たら実装完了です!
おわりに
FastAPI
とStreamlit
はいかがでしたでしょうか?どちらとも思った以上に簡単に処理を記載できるかと思います。簡単なWebサービスを作成する際に役立つフレームワークかと思いますので、この記事が少しでも参考になったら幸いです。