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 で開発するようにこのプロジェクトをセットアップできます。GitHub Codespaces では、コードスペース内でアプリをリモートでコーディング、デバッグ、実行できます。コードスペースは、クラウドでホストされる完全に構成された開発環境を提供し、ローカルセットアップの必要性を排除します。この環境には、プロジェクトの依存関係、ツール、および拡張機能が含まれており、一貫性のある再現可能な開発エクスペリエンスを保証します。リアルタイム編集、統合バージョン管理、デバッグおよびテストツールへの簡単なアクセスを提供することで、コラボレーションを効率化し、プロジェクトのセキュリティと信頼性を維持します。

: すべての GitHub.com アカウントには、Free プランまたは Pro プランに含まれる GitHub Codespaces の無料使用の月間クォータがあります。詳細については、GitHub Codespaces の課金についてを参照してください。

このチュートリアルのコードスペースをセットアップするには、このプロジェクトの GitHub リポジトリに移動します。このコードスペースには、FastAPI 開発を迅速に開始するために必要なすべての構成と依存関係が含まれています。

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

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

次に、Code > Codespaces > <dictionarybased> ブランチに Codespace を作成 を選択して、プロジェクトのコードスペースを作成して開きます。

完了したら、以下の データベースの置き換え セクションに進んでください。

ローカルマシン上の VS Code

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

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

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

  2. VS Code でこの新しいフォルダーを開きます (ファイル > フォルダーを開く...)。

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

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

アプリの作成には FastAPI、サーバーとして動作するには uvicorn、データストレージの処理と Redis データベースとの対話には Redistype-redis をインストールします。

  1. VS Code で新しいファイルを作成します (ファイル > 新しいテキストファイル または ⌘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: 環境を作成 コマンドを実行して、仮想環境を作成します。

    : この手順には数分かかる場合があります。

  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: インタープリターを選択 コマンドを実行)、仮想環境を手動で選択できます。

コーディング開始

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

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

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

  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: 構成を追加 を検索し、Python Debugger、次に FastAPI を選択します。これにより、編集できるカスタム構成ファイルが .vscode/launch.json に作成されます。カスタムポートを設定するには、"args":[]"--port=5000" を追加します。ファイルを保存し、(F5) を使用してデバッガーを再起動します。

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

    Hello World message displayed in the browser

    おめでとうございます! FastAPI アプリが起動して実行されています!

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

食料品リストアイテムのモデルを作成する

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

食料品リストアイテムのモデルを作成しましょう。ItemPayload モデルを使用して、食料品リストに追加するアイテムのデータ構造を定義します。このモデルには、item_iditem_namequantity の 3 つのフィールドがあります。

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

  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
    

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

以下の 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" を検索し、変数の型 および 関数の戻り値の型 のインレイヒントを有効にします

    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 a 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 エンドポイントの両方を使用することです。/docs エンドポイントは、利用可能なすべての API ルートに関する情報を提供し、API を操作してパラメーターと応答を探索できます。このドキュメントは、FastAPI アプリケーションで定義されたメタデータと型ヒントに基づいて動的に生成されます。

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

    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

    左側では、この時点で定義されているすべてのローカル変数とグローバル変数が、実行とデバッグ ビューの [変数] ウィンドウに表示されます。例では、ローカル変数ビューの下に 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 option displayed in the context menu when right-clicking on a line of code

    これにより、デバッグコンソールが開き、選択した式が実行されます。例では、式は予想どおり False に評価されます。

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

    デバッグビューツールバーの 続行 を選択するか、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 を使用している場合は、Docker コンテナ または GitHub コードスペース のいずれかをセットアップして Redis を操作できます。このチュートリアルでは Docker コンテナを使用しますが、GitHub コードスペースのセットアップ方法については、上記のセクションを参照してください。

それ以外の場合、Linux または macOS マシンを使用している場合は、Web サイトの指示に従って Redis をインストールし、データベースの置き換え セクションにスキップしてください。

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

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

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

要件

Dev コンテナ構成の作成

  1. コマンドパレットを開き、Dev Containers: Dev Container 構成ファイルを追加... を実行します。

  2. Python 3 を選択します

    Python 3 option selected in the Dev Containers configuration files list

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

  4. インストールする追加機能として Redis Server を選択し、OK を押し、デフォルトを維持 を選択します。

    オプションで、コンテナに含める 機能 をインストールできます。このチュートリアルでは、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 およびその他のライフサイクルスクリプトの詳細については、開発コンテナ仕様を参照してください。

    次に、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. 右下隅に表示される通知から コンテナで再度開く を選択するか、コマンドパレットから Dev Containers: コンテナで再度開く コマンドを実行します。

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

    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 拡張機能がコンテナに正常にインストールされたことを再確認してください。そうでない場合は、Dev Container にインストール を実行してインストールできます。

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

Python interpreter selection

: ステータスバーに Python インタープリター情報が見つからない場合は、Python インタープリターインジケーターをクリックするか (またはコマンドパレットから Python: インタープリターを選択 コマンドを実行)、コンテナ内の Python インタープリターを手動で選択できます。

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

データベースの置き換え

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

ID を知らなくてもアイテムを取得できる従来のデータベースとは異なり、Redis ハッシュから値を取得するには、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)
    

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

  2. エディターで "redis" にカーソルを置き、表示された電球をクリックします (または ⌘. (Windows, Linux Ctrl+.))。次に、'import redis' を追加 を選択します。

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

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

    これで、ローカルホスト (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. インレイヒントが有効になっている場合、Pylanceは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を名前と数量にもマッピングするために、各アイテムに個別のRedisハッシュを作成し、IDによる取得を容易にするために"item_id:{item_id}"をハッシュ名として使用します。また、これらの各ハッシュに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_name、およびquantity)と値(アイテムの新しく作成された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ルートと対話してこのアプリケーションをテストしてください。完了したらデバッガーを停止できます。

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

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

データが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ファイルを開き、エディターの右上隅にある実行ボタンを選択するか、コマンドパレットから**Python: ターミナルでPythonファイルを実行**コマンドを実行できます。

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

オプション: ChatGPT プラグインの作成

GitHub Codespacesを使用すると、ChatGPTプラグインを使用する際に、テスト目的でアプリケーションをホストできます。ChatGPTプラグインは、ChatGPTが既存のAPIと対話してChatGPTの能力を強化し、幅広いアクションを実行できるようにするツールです。ChatGPTプラグインは現在一般公開されていませんが、アクセスを得るためにウェイティングリストに参加できます。参加したら、以下のライブストリーム録画に従って、ChatGPT用の独自の食料品リストプラグインを作成できます。

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

次のステップ

このチュートリアルにご参加いただきありがとうございます! FastAPIとVS Codeでの使用方法について何か新しいことを学んでいただけたなら幸いです。

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

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

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

また、これらの他のVS Code Python記事も確認できます。