Skip to content

Commit

Permalink
add tutorial/extra-models.md
Browse files Browse the repository at this point in the history
  • Loading branch information
SwftAlpc committed Aug 21, 2020
1 parent f5154a5 commit 01c233f
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 0 deletions.
195 changes: 195 additions & 0 deletions docs/ja/docs/tutorial/extra-models.md
@@ -0,0 +1,195 @@
# 追加モデル

先ほどの例に続き、複数の関連モデルを持つことが一般的です。

これはユーザーモデルの場合は特にそうです。なぜなら:

* **入力モデル** にはパスワードが必要です。
* **出力モデル**にはパスワードを設定すべきではありません。
* **データベースモデル**はおそらくハッシュ化されたパスワードが必要になるでしょう。

!!! danger "危険"
ユーザーの平文のパスワードは絶対に保存しないでください。常に「安全なハッシュ」を保存し、それを確認することができます。

知らない方は、[セキュリティの章](security/simple-oauth2.md#password-hashing){.internal-link target=_blank}で「パスワードハッシュ」とは何かを学ぶことができます。

## 複数のモデル

ここでは、モデルのパスワードフィールドと使用場所でモデルがどのように見えるのか、大まかなイメージを紹介します:

```Python hl_lines="7 9 14 20 22 27 28 31 32 33 38 39"
{!../../../docs_src/extra_models/tutorial001.py!}
```

### `**user_in.dict()`について

#### Pydanticの`.dict()`

`user_in``UserIn`クラスのPydanticモデルです。

Pydanticモデルには、モデルのデータを含む`dict`を返す`.dict()`メソッドがあります。

そこで、以下のようなPydanticオブジェクト`user_in`を作成すると:

```Python
user_in = UserIn(username="john", password="secret", email="john.doe@example.com")
```

そして呼び出すと:

```Python
user_dict = user_in.dict()
```

これで変数`user_dict`のデータを持つ`dict`ができました。(これはPydanticモデルのオブジェクトの代わりに`dict`です)。

そして呼び出すと:

```Python
print(user_dict)
```

以下のようなPythonの`dict`を得ることができます:

```Python
{
'username': 'john',
'password': 'secret',
'email': 'john.doe@example.com',
'full_name': None,
}
```

#### `dict`の展開

`user_dict`のような`dict`を受け取り、それを`**user_dict`を持つ関数(またはクラス)に渡すと、Pythonはそれを「展開」します。これは`user_dict`のキーと値を直接キー・バリューの引数として渡します。

そこで上述の`user_dict`の続きを以下のように書くと:

```Python
UserInDB(**user_dict)
```

以下と同等の結果になります:

```Python
UserInDB(
username="john",
password="secret",
email="john.doe@example.com",
full_name=None,
)
```

もっと正確に言えば、`user_dict`を将来的にどんな内容であっても直接使用することになります:

```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)
```

#### 別の内容からのPydanticモデル

上述の例では`user_in.dict()`から`user_dict`をこのコードのように取得していますが:

```Python
user_dict = user_in.dict()
UserInDB(**user_dict)
```

これは以下と同等です:

```Python
UserInDB(**user_in.dict())
```

...なぜなら`user_in.dict()``dict`であり、`**`を付与して`UserInDB`を渡してPythonに「展開」させているからです。

そこで、別のPydanticモデルのデータからPydanticモデルを取得します。

#### `dict`の展開と追加引数

そして、追加のキーワード引数`hashed_password=hashed_password`を以下のように追加すると:

```Python
UserInDB(**user_in.dict(), hashed_password=hashed_password)
```

...以下のようになります:

```Python
UserInDB(
username = user_dict["username"],
password = user_dict["password"],これやばい
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)
```

!!! warning "注意"
サポートしている追加機能は、データの可能な流れをデモするだけであり、もちろん本当のセキュリティを提供しているわけではありません。

## 重複の削減

コードの重複を減らすことは、**FastAPI**の中核的なアイデアの1つです。

コードの重複が増えると、バグやセキュリティの問題、コードの非同期化問題(ある場所では更新しても他の場所では更新されない場合)などが発生する可能性が高くなります。

そして、これらのモデルは全てのデータを共有し、属性名や型を重複させています。

もっと良い方法があります。

他のモデルのベースとなる`UserBase`モデルを宣言することができます。そして、そのモデルの属性(型宣言、検証など)を継承するサブクラスを作ることができます。

データの変換、検証、文書化などはすべて通常通りに動作します。

このようにして、モデル間の違いだけを宣言することができます:

```Python hl_lines="7 13 14 17 18 21 22"
{!../../../docs_src/extra_models/tutorial002.py!}
```

## `Union`または`anyOf`

レスポンスを2つの型の`Union`と宣言することができます。

OpenAPIでは`anyOf`で定義されます。

そのためには、標準的なPythonの型ヒント<a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>を使用します:

```Python hl_lines="1 14 15 18 19 20 33"
{!../../../docs_src/extra_models/tutorial003.py!}
```

## モデルのリスト

同じように、オブジェクトのリストのレスポンスを宣言することができます。

そのためには、標準のPythonの`typing.List`を使用する:

```Python hl_lines="1 20"
{!../../../docs_src/extra_models/tutorial004.py!}
```

## 任意の`dict`を持つレスポンス

また、Pydanticモデルを使用せずに、キーと値の型だけを定義した任意の`dict`を使ってレスポンスを宣言することもできます。

これは、有効なフィールド・属性名(Pydanticモデルに必要なもの)を事前に知らない場合に便利です。

この場合、`typing.Dict`を使用することができます:

```Python hl_lines="1 8"
{!../../../docs_src/extra_models/tutorial005.py!}
```

## まとめ

複数のPydanticモデルを使用し、ケースごとに自由に継承します。

エンティティが異なる「状態」を持つことができる必要がある場合は、ケンティティごとに単一のデータモデルを持つ必要はありません。ユーザー「エンティティ」の場合と同様に、`password``password_hash`を含み、パスワードはありません。
1 change: 1 addition & 0 deletions docs/ja/mkdocs.yml
Expand Up @@ -27,6 +27,7 @@ nav:
- zh: /zh/
- チュートリアル - ユーザーガイド:
- tutorial/index.md
- tutorial/extra-models.md
markdown_extensions:
- toc:
permalink: true
Expand Down

0 comments on commit 01c233f

Please sign in to comment.