VS Codeのエージェントモードを拡張するには、を試してください!

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 拡張機能 Discussions 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 で開きます (ファイル > フォルダーを開く…)。

  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. 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))、デバッグ: 構成の追加を検索し、Python デバッガー、次に 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 アプリが動作するようになったので、FastAPI とシームレスに統合できるデータ検証および解析ライブラリであるPydantic を使用して、買い物リストアイテムを定義できます。Pydantic を使用すると、Python クラスと型ヒントを使用してデータモデルを定義でき、API リクエストで受信データ (「ペイロード」と呼ばれる) の自動検証と解析を行うことができます。

買い物リストアイテムのモデルを作成しましょう。ItemPayload モデルを使用して、買い物リストに追加するアイテムのデータ構造を定義します。このモデルには、item_iditem_name、および quantity の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
    

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 インレイ型ヒント」を検索し、変数型関数戻り値型のインレイヒントを有効にします。

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

  4. if quantity <= 0 ステートメントの横に、行番号の左余白をクリックして (または F9)、ブレークポイントを追加します。デバッガーはその行の実行前に停止するため、コードを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" button displayed next to the /items route in the Swagger UI

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

  8. 実行を選択します。

    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 Codespace のいずれかをセットアップして Redis を操作できます。このチュートリアルでは Docker コンテナーを使用しますが、GitHub Codespace のセットアップ方法については、上記のセクションを参照してください。

それ以外の場合、Linux または macOS マシンを使用している場合は、彼らのウェブサイトの指示に従って Redis をインストールし、データベースを置き換えるセクションに進むことができます。

Windows で Docker コンテナーをセットアップする

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

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

要件

開発コンテナー構成を作成する

  1. コマンドパレットを開き、開発コンテナー: 開発コンテナー構成ファイルの追加…を実行します。

  2. Python 3 を選択します。

    Python 3 option selected in the Dev Containers configuration files list

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

  4. インストールする追加機能としてRedis Server を選択し、OK を押してから、既定値を保持を選択します。

    オプションで、コンテナーに含める機能をインストールできます。このチュートリアルでは、Redis の適切な開発コンテナーセットアップをインストールして追加する、コミュニティが貢献した機能である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 ドキュメントを参照してください。

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

選択された 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)
    

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

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

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

    ヒント: 設定エディター (⌘, (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 にマッピングしていると仮定します (心配しないでください、このコードはすぐに後で追加します!)。次に、Redis の hget メソッドを呼び出すことで、リクエストで受信しているアイテム名の ID を取得できます。これにより、要求された名前がハッシュに既に存在する場合はアイテム 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_id が型 int であることを期待しています。この警告に対処するために、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 ハッシュを作成します。このハッシュ名には "item_id:{item_id}" を使用し、ID による取得を容易にします。また、これらの各ハッシュに item_namequantity フィールドを追加します。

  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 メソッドを使用して、フィールド (item_iditem_name、および quantity) と値 (アイテムの newly created ID、および提供された名前と数量) のマッピングを提供することで、アイテムを Redis ハッシュに追加します。

  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 記事も参照できます。