Visual Studio Code での FastAPI チュートリアル

FastAPI は、Python で API を構築するためのモダンで高性能な Web フレームワークです。API を迅速かつ効率的に構築できるように設計されており、自動検証、シリアライズ、API ドキュメントの自動生成などの機能を提供するため、Web サービスやマイクロサービスの構築で人気のある選択肢となっています。

この FastAPI チュートリアルでは、FastAPI を使用して買い物リストアプリを作成します。チュートリアルの最後には、Visual Studio Code のターミナル、エディター、デバッガーを使用して FastAPI を操作する方法を理解できるようになります。このチュートリアルは FastAPI の詳細な解説を目的としたものではありません。詳細については、FastAPI 公式ドキュメントを参照してください。

Python を初めて使用する場合は、言語や VS Code の Python サポートに慣れるために、まず私たちの Python チュートリアルから始めることをお勧めします。このチュートリアルは、すでに Python に慣れており、VS Code で FastAPI を扱う方法を学びたい方に適しています。

この FastAPI チュートリアルで完成したコードプロジェクトは、GitHub の python-sample-vscode-fastapi-tutorial にあります。

問題が発生した場合は、Python 拡張機能のディスカッション Q&Aで回答を検索したり、質問したりできます。

プロジェクトのセットアップ

本チュートリアルに向けてプロジェクトをセットアップする方法はいくつかあります。ここでは、GitHub Codespaces を使用する方法と、ローカルマシンの VS Code でセットアップする方法を解説します。

GitHub Codespaces

プロジェクトを GitHub Codespaces で開発するようにセットアップできます。これを使うと、codespace 内でリモートからコーディング、デバッグ、アプリの実行が可能になります。codespace はクラウドでホストされる完全に構成済みの開発環境を提供するため、ローカルでのセットアップが不要です。この環境にはプロジェクトの依存関係、ツール、拡張機能が含まれており、一貫性のある再現可能な開発体験を保証します。リアルタイム編集、統合バージョン管理、デバッグおよびテストツールへの容易なアクセスを提供し、コラボレーションを効率化しつつ、プロジェクトのセキュリティと信頼性を維持します。

注意: すべての GitHub.com アカウントには、Free プランまたは Pro プランに含まれる GitHub Codespaces の無料使用枠が月ごとに設定されています。詳細については、GitHub Codespaces の課金についてを参照してください。

このチュートリアル用の codespace をセットアップするには、プロジェクトの GitHub リポジトリにアクセスしてください。この codespace には、FastAPI 開発を素早く開始するために必要なすべての構成と依存関係が含まれています。

このチュートリアルでは、dictionarybased ブランチを選択してください。

dictionarybased branch selected in the python-sample-vscode-fastapi-tutorial GitHub repo

次に、Code > Codespaces > Create Codespace on <dictionarybased> ブランチを選択して、プロジェクト用の codespace を作成し開きます。

完了したら、以下の データベースの置換 セクションに進むことができます。

ローカルの VS Code で行う場合

このチュートリアルを VS Code で成功させるには、まず Python 開発環境をセットアップする必要があります。具体的には、以下のものが必要です。

このセクションでは、VS Code でワークスペースとして開くフォルダーを作成し、Python 仮想環境をセットアップし、プロジェクトの依存関係をインストールします。

  1. ファイルシステム上に、このチュートリアル用のプロジェクトフォルダー(例: groceries-plugin)を作成します。

  2. この新しいフォルダーを VS Code で開きます(File > Open Folder…)。

  3. Workspace Trust(ワークスペースの信頼)プロンプトが表示されたら、Yes, I trust the authors を選択して、ワークスペースが必要なリソースや拡張機能にアクセスできるようにします。Workspace Trust の詳細については、ドキュメントを参照してください。

次に、アプリケーションでインストールしたい依存関係をリストした requirements.txt ファイルを作成しましょう。requirements.txt ファイルは Python 開発における一般的な慣習であり、プロジェクトが依存するライブラリとそのバージョンを指定するために使用されます。このファイルは、プロジェクトに関わる誰でも同様の開発環境を再現できるようにし、一貫性を維持するために役立ちます。

アプリ作成のために FastAPI、サーバーとして uvicorn、データストレージの処理と Redis データベースとの通信のために Redis および type-redis をインストールします。

  1. VS Code で新しいファイルを作成します(File > New Text File または ⌘N (Windows, Linux Ctrl+N))。

  2. 以下の内容を追加します。

    fastapi
    redis
    types-redis
    uvicorn
    
  3. ファイルを保存(⌘S (Windows, Linux Ctrl+S))し、名前を requirements.txt にします。

  4. コマンドパレット(⇧⌘P (Windows, Linux Ctrl+Shift+P))を開き、Python: Create Environment コマンドを実行して仮想環境を作成します。

    注意: このステップは完了までに数分かかる場合があります。

  5. 環境の種類を尋ねられたら、Venv を選択します。

    Dropdown with "Venv" or "Conda" as options for environments that can be created with the Python: Create Environment command

  6. 次に、マシン上で利用可能な最新バージョンの Python を選択します。

    List of available global environments that can be used to create a virtual environment

  7. ドロップダウンリストから requirements.txt ファイルを選択すると、依存関係が自動的にインストールされます。その後 OK を選択します。

    Check box selected to install dependencies from requirements.txt file

仮想環境が作成され、依存関係が自動的にインストールされ、その環境が Python 拡張機能で使用するためにワークスペースに選択されます。選択されていることは、VS Code の右下隅を確認することで確認できます。

Environment in the Status bar

注意: ステータスバーに新しく作成された環境情報が見当たらない場合は、Python インタープリターのインジケーターをクリック(またはコマンドパレットから Python: Select Interpreter コマンドを実行)して、仮想環境を手動で選択できます。

コーディングの開始

アプリケーションを作成しましょう!

  1. File > New File… を使用して新しい Python ファイルを作成し、Python File を選択します。

  2. groceries-plugin フォルダーに main.py として保存(⇧⌘S (Windows, Linux Ctrl+Shift+S))します。

  3. 以下のコードを main.py に追加して保存します。

    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/")
    def root():
        return {"message": "Hello World"}
    
  4. デバッガー(F5)を起動してコードを実行します。

  5. ドロップダウンメニューから、リストにある FastAPI 構成オプションを選択します。

    Dropdown with debugger configuration options, with FastAPI being highlighted

    これにより、デバッガーを通じてアプリケーションサーバーを起動するように uvicorn を呼び出すデバッグ構成が自動的に作成され、ソースコードをステップ実行して動作を検査できるようになります。ターミナルに以下のような表示が出るはずです。

    Uvicorn server running message displayed in the terminal, with an URL to access the app

    ヒント: デフォルトのポートが既に使用されている場合は、デバッガーを停止してコマンドパレット(⇧⌘P (Windows, Linux Ctrl+Shift+P))を開き、Debug: Add Configuration を検索して、Python Debugger、次に FastAPI を選択します。これにより、編集可能なカスタム設定ファイルが .vscode/launch.json に作成されます。カスタムポートを設定するには、"args":[]"--port=5000" を追加します。ファイルを保存し、(F5)を使用してデバッガーを再起動します。

  6. ターミナル内の http://127.0.0.1:8000/ URL を Ctrl+Click して、デフォルトのブラウザーでそのアドレスを開きます。

    Hello World message displayed in the browser

    おめでとうございます!FastAPI アプリが起動しました!

  7. デバッグツールバーの Stop ボタン、または ⇧F5 (Windows, Linux Shift+F5) を使用してデバッガーを停止します。

買い物リストアイテム用のモデルを作成

FastAPI アプリが動作するようになったので、Pydantic を使用して買い物リストのアイテムを定義できます。Pydantic は、FastAPI とシームレスに統合されるデータ検証および解析ライブラリです。Pydantic を使用すると、API リクエスト内の受信データ(「ペイロード」と呼ばれます)を自動的に検証・解析するために、型ヒントを使用した Python クラスとしてデータモデルを定義できます。

買い物リストのアイテム用のモデルを作成しましょう。ItemPayload モデルを使用して、リストに追加するアイテムのデータ構造を定義します。このモデルには item_iditem_namequantity の3つのフィールドを持たせます。

  1. File > New File… で新しい Python ファイルを作成し、Python File を選択します。

  2. 以下の行を追加し、groceries-plugin フォルダーに models.py として保存(⇧⌘S (Windows, Linux Ctrl+Shift+S))します。

    from typing import Optional
    from pydantic import BaseModel
    
    class ItemPayload(BaseModel):
        item_id: Optional[int]
        item_name: str
        quantity: int
    

VS Code のデフォルトの Python 言語サーバーである Pylance は、Pydantic モデルや FastAPI を操作するのに役立つ型ヒント機能をサポートしています。これは、Pylance が Python の静的型チェッカーである Pyright 上に構築されており、コード内の型エラーを検出してバグを防ぎ、コード品質を向上させることができるためです。

以下の3つのステップはオプションですが、FastAPI はコードの読みやすさと検証を向上させるために型ヒントを多用しているため、Pylance の型チェック機能を活用してエラーを早期に検出することができます。

  1. 設定エディター(⌘, (Windows, Linux Ctrl+,))を開きます。

  2. 「python type checking mode」を検索し、基本的な型チェックを行うために basic に設定します。これで Pylance が診断や警告を表示し、型に関連する単純なエラーをキャッチするようになります。あるいは、strict に設定して、より高度な型チェックルールを適用することもできます。

    Python Analysis Type Checking Mode options (off, basic and strict) in Settings editor

  3. 次に「Python inlay type hints」を検索し、Variable Types(変数型)および Function Return Types(関数戻り値型)のインラインヒントを有効にします。

    Two Python Analysis Type Hints settings being enabled in the Settings editor: for Function Return Types and for Variable Types

ルートの作成

次に、買い物リストのアイテムを保存する場所が必要です。シンプルにするために、空の辞書から始めましょう。

  1. まず、サンプルに必要なすべてのパッケージをインポートしましょう。main.py ファイルを開き、最初のインポート行を以下の行に置き換えます。

    from fastapi import FastAPI, HTTPException
    
    from models import ItemPayload
    
  2. 次に、app = FastAPI() のすぐ下に以下の行を追加します。

    grocery_list: dict[int, ItemPayload] = {}
    

    これで、int 型(アイテム ID)をキーとし、ItemPayload 型の値を格納する新しい空の辞書が作成されます。

    FastAPI アプリケーションでルートを定義します。Web アプリケーションのコンテキストにおいて、ルートは特定の URL をそれを処理するコードにマッピングする経路のようなものです。これらのルートは、アプリケーション内のさまざまな機能へのエントリポイントとして機能します。Web ブラウザーなどのクライアントが特定の URL でリクエストを送信すると、FastAPI はその URL に基づいてリクエストを適切な関数(ルートハンドラーまたはビュー関数)にルーティングし、その関数がリクエストを処理してレスポンスを生成します。

    個々のアイテムを追加および取得し、買い物リストの全アイテムを返すためのルート定義に進みましょう。

  3. main.py ファイルの最後に以下のルートを追加します。

    # Route to add an item
    @app.post("/items/{item_name}/{quantity}")
    def add_item(item_name: str, quantity: int):
        if quantity <= 0:
            raise HTTPException(status_code=400, detail="Quantity must be greater than 0.")
        # if item already exists, we'll just add the quantity.
        # get all item names
        items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()}
        if item_name in items_ids.keys():
            # get index of item_name in item_ids, which is the item_id
            item_id = items_ids[item_name]
            grocery_list[item_id].quantity += quantity
    # otherwise, create a new item
        else:
            # generate an ID for the item based on the highest ID in the grocery_list
            item_id = max(grocery_list.keys()) + 1 if grocery_list else 0
            grocery_list[item_id] = ItemPayload(
                item_id=item_id, item_name=item_name, quantity=quantity
            )
    
        return {"item": grocery_list[item_id]}
    

    前のセクションで型ヒントを有効にしている場合、Pylance が関数の戻り値型や item_ids および item_id の型に対してインラインヒントを追加していることに気付くかもしれません。必要に応じて、各候補をダブルクリックしてコードに挿入できます。

    Inlay function return and variable type hints being displayed by Pylance throughout the sample code

    このルートが期待通りに動作するか確認しましょう。最も早い方法は、VS Code のデバッガーと FastAPI の /docs エンドポイントの両方を使用することです。このエンドポイントは利用可能なすべての API ルートに関する情報を提供し、API を操作してパラメーターやレスポンスを調査できます。このドキュメントは、FastAPI アプリケーションで定義されたメタデータと型ヒントに基づいて動的に生成されます。

  4. 行番号の左側の余白をクリックするか(F9)、if quantity <= 0 文の横にブレークポイントを追加します。デバッガーはその行の実行前に停止するため、コードを一行ずつ検査できます。

    Breakpoint set next to the first line in the add_item function

  5. デバッガー(F5)を開始し、ブラウザーで http://127.0.0.1:8000/docs にアクセスします。

    アプリで利用可能な2つのエンドポイント(/items とルート /)を備えた Swagger インターフェイスが表示されるはずです。

    Swagger UI displaying two endpoints: /items and /

  6. /items ルートの横にある下向き矢印を選択して展開し、右側に表示される Try it out ボタンを選択します。

    "Try it out" button displayed next to the /items route in the Swagger UI

  7. item_name フィールドに文字列を渡し、quantity に数値を入力して買い物リストアイテムを追加します。例として、item_name に「apple」、quantity に「2」を入力できます。

  8. Execute を選択します。

    Execute button displayed below the /items route

  9. VS Code を再び開くと、先ほど設定したブレークポイントでデバッガーが停止しているのがわかります。

    Debugger stopped at the breakpoint set in the add_item function

    左側の Run and Debug ビューにある「Variables」ウィンドウには、この時点で定義されているすべてのローカルおよびグローバル変数が表示されます。この例では、ローカル変数ビューで item_name が「apple」、quantity が 2 に設定され、グローバル変数ビューには空の grocery_list 辞書が表示されています。

    Variables window displayed in the Run and Debug view, with the item and grocery_list variables highlighted

    次に、VS Code のデバッグコンソールを使用して調査を行いましょう。

  10. quantity <= 0 文を選択し、エディターを右クリックして Evaluate in Debug Console を選択します。

    Evaluate in Debug Console option displayed in the context menu when right-clicking on a line of code

    これによりデバッグコンソールが開き、選択した式が実行されます。この例で期待される通り、式は False と評価されます。

    デバッグコンソールは、式を素早くテストし、ブレークポイント時点でのコードの状態をよりよく理解するための強力なツールです。関数を呼び出したり変数を表示したりするなど、任意のコードを実行することもできます。VS Code での Python デバッグの詳細については、Python チュートリアルを参照してください。

    デバッグビューのツールバーで Continue を選択するか、F5 を押すことで、コードの実行を継続できます。

    最後に、すべてのアイテムや特定のアイテムをリスト表示し、買い物リストから削除できるようにするための残りのルートをアプリケーションに追加しましょう。デバッガーは実行したままにしてください。次のステップで変更を保存すると、アプリケーションが自動的に再読み込みされます。

  11. main.py の内容を以下のコードで置き換えます。

    from fastapi import FastAPI, HTTPException
    
    from models import ItemPayload
    
    app = FastAPI()
    
    grocery_list: dict[int, ItemPayload] = {}
    
    # Route to add an item
    @app.post("/items/{item_name}/{quantity}")
    def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]:
        if quantity <= 0:
            raise HTTPException(status_code=400, detail="Quantity must be greater than 0.")
        # if item already exists, we'll just add the quantity.
        # get all item names
        items_ids: dict[str, int] = {
            item.item_name: item.item_id if item.item_id is not None else 0
            for item in grocery_list.values()
        }
        if item_name in items_ids.keys():
            # get index of item_name in item_ids, which is the item_id
            item_id: int = items_ids[item_name]
            grocery_list[item_id].quantity += quantity
        # otherwise, create a new item
        else:
            # generate an ID for the item based on the highest ID in the grocery_list
            item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0
            grocery_list[item_id] = ItemPayload(
                item_id=item_id, item_name=item_name, quantity=quantity
            )
    
        return {"item": grocery_list[item_id]}
    
    
    # Route to list a specific item by ID
    @app.get("/items/{item_id}")
    def list_item(item_id: int) -> dict[str, ItemPayload]:
        if item_id not in grocery_list:
            raise HTTPException(status_code=404, detail="Item not found.")
        return {"item": grocery_list[item_id]}
    
    
    # Route to list all items
    @app.get("/items")
    def list_items() -> dict[str, dict[int, ItemPayload]]:
        return {"items": grocery_list}
    
    
    # Route to delete a specific item by ID
    @app.delete("/items/{item_id}")
    def delete_item(item_id: int) -> dict[str, str]:
        if item_id not in grocery_list:
            raise HTTPException(status_code=404, detail="Item not found.")
        del grocery_list[item_id]
        return {"result": "Item deleted."}
    
    
    # Route to remove some quantity of a specific item by ID
    @app.delete("/items/{item_id}/{quantity}")
    def remove_quantity(item_id: int, quantity: int) -> dict[str, str]:
        if item_id not in grocery_list:
            raise HTTPException(status_code=404, detail="Item not found.")
        # if quantity to be removed is higher or equal to item's quantity, delete the item
        if grocery_list[item_id].quantity <= quantity:
            del grocery_list[item_id]
            return {"result": "Item deleted."}
        else:
            grocery_list[item_id].quantity -= quantity
        return {"result": f"{quantity} items removed."}
    
    
  12. ファイルを保存(⌘S (Windows, Linux Ctrl+S))します。アプリケーションが自動的に再読み込みされるはずです。

再度 /docs ページを開き、新しいルートをテストします。デバッガーとデバッグコンソールを使用してコードの実行をより深く理解しましょう。完了したらデバッガーを停止(⇧F5 (Windows, Linux Shift+F5))できます。ステップ 4 で追加したブレークポイントはクリックして削除することも可能です。

おめでとうございます!買い物リストへのアイテム追加、リスト表示、削除を行うルートを備えた、動作する FastAPI アプリケーションが完成しました。

データストレージのセットアップ

ここまでで、基本的な機能を備えたアプリケーションの動作バージョンが完成しています。このセクションでは永続化のためのデータストレージのセットアップ方法を解説しますが、すでに学習した内容で満足であればスキップしても構いません。

これまではデータを辞書に保存していましたが、アプリケーションを再起動するとすべてのデータが失われてしまうため、これは理想的ではありません。

データを永続化するために、オープンソースのインメモリデータ構造ストアである Redis を使用します。その速度と汎用性から、Redis は Web アプリケーション、リアルタイム分析システム、キャッシュ層、本チュートリアルなど、幅広いアプリケーションでデータストレージシステムとして一般的に使用されています。

既存のテンプレートを使用してすでに GitHub Codespaces で作業している場合は、データベースの置換 セクションに直接進むことができます。

Windows で作業している場合、Redis は Docker コンテナー または GitHub Codespace を設定することで利用できます。このチュートリアルでは Docker コンテナーを使用しますが、GitHub Codespace のセットアップ方法については上記のセクションを参照してください。

それ以外の場合、Linux または macOS マシンを使用しているなら、公式サイトの指示に従って Redis をインストールし、その後 データベースの置換 セクションに進んでください。

Windows での Docker コンテナーのセットアップ

VS Code の Dev Containers 拡張機能は、プロジェクトとその依存関係、および必要なすべてのツールを1つの tidy なコンテナーに統合するための合理化されたアプローチを提供し、フル機能の開発環境を作成します。この拡張機能を使用すると、プロジェクトをコンテナー内(またはマウントされたコンテナー)で開き、そのフル機能セットを使用できます。

以下の手順では、マシンに以下の要件がインストールされていることを確認してください。

要件

Dev コンテナー構成を作成する

  1. コマンドパレットを開き、Dev Containers: Add Dev Container Configuration Files… を実行します。

  2. Python 3 を選択します。

    Python 3 option selected in the Dev Containers configuration files list

  3. デフォルトのバージョンを選択します。

  4. 追加機能としてインストールするものに Redis Server を選択し、OK を押し、次に Keep Defaults を選択します。

    オプションでコンテナーに含める機能 (Features) をインストールできます。本チュートリアルでは、Redis 用の適切な dev コンテナー設定をインストールして追加するコミュニティ貢献機能である Redis Server をインストールします。

    Redis Server option selected in the Dev Containers configuration files list

    これにより、ワークスペースに .devcontainer フォルダーと devcontainer.json ファイルが作成されます。このファイルを編集して、コンテナーのセットアップに VS Code 拡張機能やプロジェクトの依存関係のインストール手順が含まれるようにしましょう。

  5. devcontainer.json ファイルを開きます。

  6. "features" : { ... } エントリの後に「,」を追加し、ファイルに設定を追加できるようにします。

    次に、devcontainer.json ファイルの postCreateCommand プロパティに必要な依存関係インストールコマンドを追加して、コンテナーのセットアップ完了後にアプリケーションを実行できるようにします。

  7. 以下のコンテンツを見つけ、その行のコメント(//)を削除して、コンテナー作成時に依存関係がインストールされるようにします。

    "postCreateCommand": "pip3 install --user -r requirements.txt",
    

    postCreateCommand やその他のライフサイクルスクリプトの詳細については、Development Containers Specification を参照してください。

    次に、customizations プロパティを使用して、コンテナーにインストールしたい VS Code 拡張機能を追加します。

  8. 以下の設定を devcontainer.json に追加します。

        // Use 'postCreateCommand' to run commands after the container is created.
        "postCreateCommand": "pip3 install --user -r requirements.txt",
    
        // Configure tool-specific properties.
        "customizations": {
            "vscode": {
                "extensions": [
                    "ms-python.python", //Python extension ID
                    "ms-python.vscode-pylance" //Pylance extension ID
                ]
            }
        }
    
  9. ファイルを保存します。

  10. 右下に表示される通知から Reopen in Container を選択するか、コマンドパレットから Dev Containers: Reopen in Container コマンドを実行します。

    注意: インターネット速度やマシンのパフォーマンスによっては、コンテナーのビルドに数分かかる場合があります。

    dev コンテナーの構成の詳細については、Dev Containers ドキュメントを参照してください。

完了すると、Python 3 と Redis Server がインストールされた、完全に構成済みの Linux ベースのワークスペースが用意されます。

コンテナーがセットアップされると、VS Code の左下にインジケーターが表示されます。

Dev Containers indicator displayed on the bottom left corner of VS Code

注意: 拡張機能ビュー(⇧⌘X (Windows, Linux Ctrl+Shift+X))を開いて検索し、Python および Pylance 拡張機能がコンテナーに正常にインストールされたか再確認してください。インストールされていない場合は、Install in Dev Container を実行してインストールできます。

選択された Python インタープリター情報は、右下のステータスバーで確認でき、devcontainer.json ファイルで指定されたバージョンと一致しているはずです。

Python interpreter selection

注意: ステータスバーに Python インタープリター情報がない場合は、Python インタープリターのインジケーターをクリック(またはコマンドパレットから Python: Select Interpreter コマンドを実行)して、コンテナー内の Python インタープリターを手動で選択してください。

これで次のセクションに進む準備ができました。データストレージを置き換えます。

データベースの置換

現在、買い物リストアイテムを保存するために辞書を使用していますが、これを Redis データベースに置き換えます。このチュートリアルでは、複数のキーと値のペアを格納できるデータ構造である Redis ハッシュを使用してデータを保存します。

ID を知らなくてもアイテムを取得できる従来のデータベースとは異なり、Redis ハッシュから値を取得するにはハッシュキーを知る必要があります。このチュートリアルでは、アイテムを名前で取得して ID にマッピングするために item_name_to_id というハッシュを作成します。さらに、ID でアイテムを取得し、名前と数量にマッピングするために他のハッシュを作成します。各アイテムハッシュは item_id:{item_id} と名付けられ、item_namequantity の2つのフィールドを持ちます。

まず、辞書を Redis サーバーに接続する Redis クライアントオブジェクトに置き換えるところから始めましょう。

  1. main.py ファイルの先頭にある grocery_list: dict[int, ItemPayload] = {} を、以下の行に置き換えます。

    redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True)
    

    Redis がまだインポートされていないため、Pylance がエラーメッセージを表示します。

  2. エディターで「redis」にカーソルを合わせ、表示された電球をクリックするか(⌘. (Windows, Linux Ctrl+.))、Add 'import redis' を選択します。

    Light bulb displayed next to the Redis variable, with the option to add the import statement

    ヒント: Pylance が自動的にインポートを追加するように設定できます。設定エディター(⌘, (Windows, Linux Ctrl+,))で Auto Import Completions 設定を探し、有効にしてください。

    これで、ローカルホスト(host="0.0.0.0")で実行され、ポート 6379(port=6379)をリッスンしている Redis サーバーに接続する Redis クライアントオブジェクトができました。db パラメーターは使用する Redis データベースを指定します。Redis は複数のデータベースをサポートしており、このコードではデフォルトのデータベースである 0 を使用します。また、レスポンスを(バイトではなく)文字列としてデコードするために decode_responses=True を渡しています。

    最初のルート add_item をもう少し置き換えましょう。提供されたアイテム名を見つけるために辞書内のすべてのキーを調べる代わりに、Redis ハッシュからその情報を直接取得できます。

    item_name_to_id ハッシュがすでに存在し、アイテム名を ID にマッピングしていると仮定します(このコードはすぐに後で追加します!)。リクエストで受け取ったアイテム名の ID は、Redis の hget メソッドを呼び出すことで取得できます。これにより、要求された名前がハッシュに存在する場合はアイテム ID が返され、存在しない場合は None が返されます。

  3. 以下の内容の行を削除します。

    items_ids = {item.item_name: item.item_id if item.item_id is not None else 0 for item in grocery_list.values()}
    

    そして以下に置き換えます。

      item_id = redis_client.hget("item_name_to_id", item_name)
    

    この変更により Pylance が問題を発生させていることに注意してください。これは、hget メソッドが str または None (アイテムが存在しない場合) を返すためです。しかし、まだ置き換えていない以下のコードは item_idint 型であることを期待しています。この警告に対処するため、item_id シンボルを名前変更しましょう。

  4. item_iditem_id_str に変更します。

  5. インラインヒントが有効な場合、item_id_str の横に変数型ヒントが表示されるはずです。ダブルクリックして受け入れることができます。

    Variable type hint displayed next to the item_id_str variable

  6. アイテムが存在しない場合、item_id_strNone になります。したがって、以下の内容の行を削除できます。

    if item_name in items_ids.keys():
    

    そして以下に置き換えます。

    if item_id_str is not None:
    

    アイテム ID が文字列として取得できたので、それを int に変換してアイテムの数量を更新する必要があります。現在、Redis ハッシュはアイテム名を ID にマッピングするだけです。ID を名前や数量にマッピングするためにも、ID での取得を容易にするために "item_id:{item_id}" をハッシュ名として使用し、アイテムごとに個別の Redis ハッシュを作成します。各ハッシュには item_name および quantity フィールドを追加します。

  7. if ブロック内のコードを削除します。

    item_id: int = items_ids[item_name]
    grocery_list[item_id].quantity += quantity
    

    そして以下を追加し、item_idint に変換し、Redis の hincrby メソッドを呼び出してアイテムの数量を増やします。このメソッドは、"quantity" フィールドの値をリクエスト内の指定量(quantity)だけ増やします。

    item_id = int(item_id_str)
    redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity)
    

    これで、アイテムが存在しない場合(item_id_strNone の場合)のコードを置き換えるだけです。この場合、新しい item_id を生成し、アイテム用の新しい Redis ハッシュを作成して、提供されたアイテム名と数量を追加します。

    新しい item_id を生成するために、Redis の incr メソッドを使用し、"item_ids" という新しいハッシュを渡します。このハッシュは最後に生成された ID を保存するために使用されるため、新しいアイテムを作成するたびにインクリメントでき、すべてがユニークな ID を持つことを保証できます。

  8. 以下の内容の行を削除します。

    item_id: int = max(grocery_list.keys()) + 1 if grocery_list else 0
    

    そして以下を追加します。

    item_id: int = redis_client.incr("item_ids")
    

    この incr 呼び出しが item_ids キーで初めて実行されると、Redis はキーを作成し、値を 1 にマッピングします。その後、実行されるたびに保存された値が 1 ずつ増加します。

    次に、hset メソッドを使用して Redis ハッシュにアイテムを追加します。フィールド(item_iditem_namequantity)のマッピングと、値(アイテムの新規作成された ID、提供された名前、および数量)を提供します。

  9. 以下の内容の行を削除します。

    grocery_list[item_id] = ItemPayload(
            item_id=item_id, item_name=item_name, quantity=quantity
        )
    

    そして以下のように置き換えます。

    redis_client.hset(
                f"item_id:{item_id}",
                mapping={
                    "item_id": item_id,
                    "item_name": item_name,
                    "quantity": quantity,
                })
    

    最後に、最初に参照した item_name_to_id ハッシュを設定することで、新規作成された ID をアイテム名にマッピングするだけです。

  10. この行をルートの最後、else ブロック内に追加します。

    redis_client.hset("item_name_to_id", item_name, item_id)
    
  11. 以下の内容の行を削除します。

    return {"item": grocery_list[item_id]}
    

    そして以下に置き換えます。

    return {"item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)}
    
  12. 必要であれば、他のルートに対しても同様の置換を試みることができます。そうでなければ、ファイル全体の内容を以下の行で置き換えることも可能です。

    import redis
    from fastapi import FastAPI, HTTPException
    
    from models import ItemPayload
    
    app = FastAPI()
    
    redis_client = redis.StrictRedis(host="0.0.0.0", port=6379, db=0, decode_responses=True)
    
    # Route to add an item
    @app.post("/items/{item_name}/{quantity}")
    def add_item(item_name: str, quantity: int) -> dict[str, ItemPayload]:
        if quantity <= 0:
            raise HTTPException(status_code=400, detail="Quantity must be greater than 0.")
    
        # Check if item already exists
        item_id_str: str | None = redis_client.hget("item_name_to_id", item_name)
    
        if item_id_str is not None:
            item_id = int(item_id_str)
            redis_client.hincrby(f"item_id:{item_id}", "quantity", quantity)
        else:
            # Generate an ID for the item
            item_id: int = redis_client.incr("item_ids")
            redis_client.hset(
                f"item_id:{item_id}",
                mapping={
                    "item_id": item_id,
                    "item_name": item_name,
                    "quantity": quantity,
                },
            )
            # Create a set so we can search by name too
            redis_client.hset("item_name_to_id", item_name, item_id)
    
        return {
            "item": ItemPayload(item_id=item_id, item_name=item_name, quantity=quantity)
        }
    
    
    # Route to list a specific item by ID but using Redis
    @app.get("/items/{item_id}")
    def list_item(item_id: int) -> dict[str, dict[str, str]]:
        if not redis_client.hexists(f"item_id:{item_id}", "item_id"):
            raise HTTPException(status_code=404, detail="Item not found.")
        else:
            return {"item": redis_client.hgetall(f"item_id:{item_id}")}
    
    
    @app.get("/items")
    def list_items() -> dict[str, list[ItemPayload]]:
        items: list[ItemPayload] = []
        stored_items: dict[str, str] = redis_client.hgetall("item_name_to_id")
    
        for name, id_str in stored_items.items():
            item_id: int = int(id_str)
    
            item_name_str: str | None = redis_client.hget(f"item_id:{item_id}", "item_name")
            if item_name_str is not None:
                item_name: str = item_name_str
            else:
                continue  # skip this item if it has no name
    
            item_quantity_str: str | None = redis_client.hget(
                f"item_id:{item_id}", "quantity"
            )
            if item_quantity_str is not None:
                item_quantity: int = int(item_quantity_str)
            else:
                item_quantity = 0
    
            items.append(
                ItemPayload(item_id=item_id, item_name=item_name, quantity=item_quantity)
            )
    
        return {"items": items}
    
    
    # Route to delete a specific item by ID but using Redis
    @app.delete("/items/{item_id}")
    def delete_item(item_id: int) -> dict[str, str]:
        if not redis_client.hexists(f"item_id:{item_id}", "item_id"):
            raise HTTPException(status_code=404, detail="Item not found.")
        else:
            item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name")
            redis_client.hdel("item_name_to_id", f"{item_name}")
            redis_client.delete(f"item_id:{item_id}")
            return {"result": "Item deleted."}
    
    
    # Route to remove some quantity of a specific item by ID but using Redis
    @app.delete("/items/{item_id}/{quantity}")
    def remove_quantity(item_id: int, quantity: int) -> dict[str, str]:
        if not redis_client.hexists(f"item_id:{item_id}", "item_id"):
            raise HTTPException(status_code=404, detail="Item not found.")
    
        item_quantity: str | None = redis_client.hget(f"item_id:{item_id}", "quantity")
    
        # if quantity to be removed is higher or equal to item's quantity, delete the item
        if item_quantity is None:
            existing_quantity: int = 0
        else:
            existing_quantity: int = int(item_quantity)
        if existing_quantity <= quantity:
            item_name: str | None = redis_client.hget(f"item_id:{item_id}", "item_name")
            redis_client.hdel("item_name_to_id", f"{item_name}")
            redis_client.delete(f"item_id:{item_id}")
            return {"result": "Item deleted."}
        else:
            redis_client.hincrby(f"item_id:{item_id}", "quantity", -quantity)
            return {"result": f"{quantity} items removed."}
    
    
  13. デバッガーを再実行して、/docs ルートを操作してアプリケーションをテストします。完了したらデバッガーを停止してください。

おめでとうございます!買い物リストへのアイテム追加、リスト表示、削除を行うルートを備え、データが Redis データベースに永続化される、動作する FastAPI アプリケーションが完成しました。

オプション:データベース削除のセットアップ

Redis によってデータが永続化されるようになったので、すべてのテストデータを消去するスクリプトを作成したいかもしれません。そのために、flushdb.py という新しいファイルを作成し、以下の内容を記述します。

import redis

redis_client = redis.StrictRedis(host='0.0.0.0', port=6379, db=0, decode_responses=True)
redis_client.flushdb()

データベースをリセットしたいときは、VS Code で flushdb.py ファイルを開き、エディターの右上にある Run ボタンを選択するか、コマンドパレットから Python: Run Python File in Terminal コマンドを実行します。

これは現在のデータベース内のすべてのキーを削除するため、本番環境で行うとデータ損失につながる可能性があることに注意し、慎重に行ってください。

オプション:GPT Action の作成

GitHub Codespaces を使用すると、GPT Actions を使用する際のテスト目的でアプリケーションをホストできます。GPT Actions は、ChatGPT が既存の API と対話して機能を強化し、幅広いアクションを実行できるようにするツールです。以下のライブストリーム録画に従って、ChatGPT 用の自分だけの買い物リストプラグインを作成できます。

注意: すべての個人用 GitHub.com アカウントには、Free または Pro プランに含まれる GitHub Codespaces の無料使用枠が月ごとに設定されています。詳細については、GitHub Codespaces の課金についてを参照してください。

次のステップ

このチュートリアルに最後までお付き合いいただきありがとうございました!FastAPI に関する新しい知識と、VS Code での使い方が身についたことを願っています。

このチュートリアルで完成したコードプロジェクトは GitHub で確認できます: python-sample-vscode-fastapi-tutorial

FastAPI の詳細については、公式ドキュメントを参照してください。

本番 Web サイトでアプリを試すには、チュートリアル Docker コンテナーを使用して Azure App Service に Python アプリをデプロイする を確認してください。

その他の VS Code Python 記事も確認できます。

© . This site is unofficial and not affiliated with Microsoft.