49
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

StreamlitとFastAPIを使ってメモアプリを作ってみた

Last updated at Posted at 2022-03-11

はじめに

今回PythonのライブラリStreamlitとWebフレームワークFastAPIを使って簡単なメモアプリを作ってみました。即席でWebアプリケーションを作るのに有用かつ、お手軽に作成できたので紹介させてください。

目標

メモ登録・一覧画面をStreamlitFastAPIを使用して作成していきます。
イメージとして下記2画面です。
メモ登録画面
image.png
メモ一覧画面
image.png

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に下記コードを記載していきましょう。

front.py
import streamlit as st

st.title('Hello World')

Hello Worldを表示するのに必要なコードはこれだけです。コマンドを実行してサーバーを立ち上げてみましょう。

streamlit run front.py

Untitled.png
Hello Worldが表示されたら成功です!

FastAPI動作確認

main.pyに下記コードを記載します。

main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def get():
    return 'Hello World'

コードが書けたらこちらも実行してみましょう。

uvicorn main:app --reload

Untitled 1.png

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に記載していきます。

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だけテーブルに持ちます。

model.py
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に記載していきます。

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に記載していきます。

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に記載してバックエンドの実装は完了です。

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

Untitled 2.png

Swagger UIが表示され、実際にリクエストを送ってテストすることもできます。
今回は紹介だけに留めてメモ参照・登録のリクエストを実行していきます。

メモ登録(POST:http://127.0.0.1:8000/memos)

Body
{
  "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())で確認できるようにします。

main.py
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'))

動作確認

メモ登録・参照してみましょう!

メモ登録画面

Untitled 3.png

成功した場合、レスポンスを画面上で確認できます。

メモ一覧画面

Untitled 4.png

メモ一覧および先ほど登録したメモを確認出来たら実装完了です!

おわりに

FastAPIStreamlitはいかがでしたでしょうか?どちらとも思った以上に簡単に処理を記載できるかと思います。簡単なWebサービスを作成する際に役立つフレームワークかと思いますので、この記事が少しでも参考になったら幸いです。

49
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?