コンテンツにスキップ

現在のユーザーの取得

一つ前の章では、(依存性注入システムに基づいた)セキュリティシステムは、 path operation関数str として token を与えていました:

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

しかし、それはまだそんなに有用ではありません。

現在のユーザーを取得するようにしてみましょう。

ユーザーモデルの作成

まずは、Pydanticのユーザーモデルを作成しましょう。

ボディを宣言するのにPydanticを使用するのと同じやり方で、Pydanticを別のどんなところでも使うことができます:

from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

依存関係 get_current_user を作成

依存関係 get_current_user を作ってみましょう。

依存関係はサブ依存関係を持つことができるのを覚えていますか?

get_current_user は前に作成した oauth2_scheme と同じ依存関係を持ちます。

以前直接 path operation の中でしていたのと同じように、新しい依存関係である get_current_userstr として token を受け取るようになります:

from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

ユーザーの取得

get_current_user は作成した(偽物の)ユーティリティ関数を使って、 str としてトークンを受け取り、先ほどのPydanticの User モデルを返却します:

from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

現在のユーザーの注入

ですので、 get_current_user に対して同様に path operation の中で Depends を利用できます。

from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

Pydanticモデルの User として、 current_user の型を宣言することに注意してください。

その関数の中ですべての入力補完や型チェックを行う際に役に立ちます。

豆知識

リクエストボディはPydanticモデルでも宣言できることを覚えているかもしれません。

ここでは Depends を使っているおかげで、 FastAPI が混乱することはありません。

確認

依存関係システムがこのように設計されているおかげで、 User モデルを返却する別の依存関係(別の"dependables")を持つことができます。

同じデータ型を返却する依存関係は一つだけしか持てない、という制約が入ることはないのです。

別のモデル

これで、path operation関数 の中で現在のユーザーを直接取得し、Depends を使って、 依存性注入 レベルでセキュリティメカニズムを処理できるようになりました。

そして、セキュリティ要件のためにどんなモデルやデータでも利用することができます。(この場合は、 Pydanticモデルの User

しかし、特定のデータモデルやクラス、型に制限されることはありません。

モデルを、 idemail は持つが、 username は全く持たないようにしたいですか? わかりました。同じ手段でこうしたこともできます。

ある str だけを持ちたい? あるいはある dict だけですか? それとも、データベースクラスのモデルインスタンスを直接持ちたいですか? すべて同じやり方で機能します。

実際には、あなたのアプリケーションにはログインするようなユーザーはおらず、単にアクセストークンを持つロボットやボット、別のシステムがありますか?ここでも、全く同じようにすべて機能します。

あなたのアプリケーションに必要なのがどんな種類のモデル、どんな種類のクラス、どんな種類のデータベースであったとしても、 FastAPI は依存性注入システムでカバーしてくれます。

コードサイズ

この例は冗長に見えるかもしれません。セキュリティとデータモデルユーティリティ関数および path operations が同じファイルに混在しているということを覚えておいてください。

しかし、ここに重要なポイントがあります。

セキュリティと依存性注入に関するものは、一度だけ書きます。

そして、それは好きなだけ複雑にすることができます。それでも、一箇所に、一度だけ書くのです。すべての柔軟性を備えます。

しかし、同じセキュリティシステムを使って何千ものエンドポイント(path operations)を持つことができます。

そして、それらエンドポイントのすべて(必要な、どの部分でも)がこうした依存関係や、あなたが作成する別の依存関係を再利用する利点を享受できるのです。

さらに、こうした何千もの path operations は、たった3行で表現できるのです:

from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

まとめ

これで、 path operation関数 の中で直接現在のユーザーを取得できるようになりました。

既に半分のところまで来ています。

あとは、 usernamepassword を実際にそのユーザーやクライアントに送る、 path operation を追加する必要があるだけです。

次はそれを説明します。