【FastAPI】Pydanticを使ってバリデーション：none is not an allowed valueエラーの解決方法

# 概要
FastAPIでは`Pydantic`というライブラリがあり、データのバリデーションを簡単に行うことができます。今回は、その`Pydantic`を利用している実装中に`none is not an allowed value`というエラーに遭遇したのでその解決方法を紹介します。

## レスポンスが成功するサンプルコード

```py
from typing import Any, Optional, Union
from pydantic import BaseModel

class ResultResponse(BaseModel):
    request_id: Optional[str] = None
    status: int = ''
    msg: str = ''
    info: Optional[Union[int, str]]
    flag: Optional[Any] = None

print(f"ResultResponse: {ResultResponse}, type: {type(ResultResponse)}")
# ResultResponse: <class '__main__.ResultResponse'>, type: <class 'pydantic.main.ModelMetaclass'>

response_request_id = '001'
response_status = 1234
response_message = 'これはメッセージです'
response_info = None
response_flag = False

response_data=ResultResponse( \
        request_id=response_request_id, \
        status=response_status, \
        msg=response_message, \
        info=response_info, \
        flag=response_flag
        )

print(f"response_data: {response_data}, type: {type(response_data)}")
# response_data: request_id='001' status=1234 msg='これはメッセージです' info=None flag=False, type: <class '__main__.ResultResponse'>
```

### 補足：`pydantic`モデルと`BaseModel`クラス
FastAPIでは、APIのリクエストデータを自動的に`Pydantic`モデルとして解釈し、バリデーションを行います。また、レスポンスデータも`Pydantic`モデルとして定義することができ、FastAPIは自動的にデータをシリアライズしてJSON形式で返します。

`BaseModel`クラスは`pydantic`が提供する特別なクラスであり、データモデルを定義する際に使用します。`BaseModel`を使用することで、データが指定したルールに適合しているかどうかを簡単にチェックできます。不適切なデータが与えられた場合、自動的に`ValidationError`が発生します。

`ResultResponse`の`type`をprintしていますが、`BaseModel`クラスを継承したクラスを定義する際にそのクラスが `pydantic.main.ModelMetaclass`を持つようになります。このメタクラスは、クラス定義時にフィールドの型ヒントやバリデーションルールを解析し、データモデルとしての振る舞いを付与します。

### 補足：データモデルでフィールド定義
上記のコードでは、`pydantic`ライブラリを使用して `ResultResponse` というデータモデルを定義していますが、フィールド定義では`int`や`str`などの型を指定しています。これにより、データの整合性を保証し、バリデーションエラーを事前に検知することができるわけです。

サンプルコードでは、`ResultResponse`データモデルで以下のフィールド定義をしています。

`request_id`フィールド: 文字列型の値を持ち、デフォルト値として`None`を指定。
`status`フィールド: 整数型の値を持ち、デフォルト値として空の文字列`''`を指定。
`msg`フィールド: 文字列型の値を持ち、デフォルト値として空の文字列`''`を指定。

### `Union` とは？
`info`フィールドでは、`Optional[Union[int, str]]`と定義しています。この場合、数型の値、文字列型の値、または`None`のいずれかを受け入れることができます(デフォルト値は指定していません)。

### `Any` とは？
`flag`フィールドでは、`Optional[Any]`型を定義しています。`Any`型は任意の型を表しますので、任意の型の値もしくは `None`を受け入れることができます。デフォルト値として`None`が指定しています。


この状態であれば、`response_data`インスタンスの中身は正常に出力されます。
ただし、以下の場合だと失敗しました。

## バリデーションエラーになるサンプルコード

```test-pydantic.py
from typing import Any, Optional, Union
from pydantic import BaseModel

class ResultResponse(BaseModel):
    request_id: str = ''　#　デフォルト値を`''`に変更。
    status: int = ''
    msg: str = ''
    info: Optional[Union[int, str]]
    flag: Optional[Any] = None

print(f"ResultResponse: {ResultResponse}, type: {type(ResultResponse)}")
# ResultResponse: <class '__main__.ResultResponse'>, type: <class 'pydantic.main.ModelMetaclass'>

response_request_id = None　#　Noneに変更
response_status = 1234
response_message = 'これはメッセージです'
response_info = None
response_flag = False

response_data=ResultResponse( \
        request_id=response_request_id, \
        status=response_status, \
        msg=response_message, \
        info=response_info, \
        flag=response_flag
        )

print(f"response_data: {response_data}, type: {type(response_data)}")
# none is not an allowed value (type=type_error.none.not_allowed)
```

エラー内容全文は以下の通りです。

```
Traceback (most recent call last):
  File "/Users/xxxx/Projects/TEST/test-pydantic.py", line 19, in <module>
    response_data=ResultResponse( \
  File "pydantic/main.py", line 342, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ResultResponse
request_id
  none is not an allowed value (type=type_error.none.not_allowed)
```

変更した点は、データモデル`ResultResponse`で`request_id`のデフォルト値を`request_id: str = ''`に変更し、`response_request_id = None`というように`None`を渡すようにした2点。

### `str = ''`と`Optional[str] = None`の違い
なぜこれで`none is not an allowed value`というエラーになったかというと、`request_id: str = ''`の場合、`None`を許容しないからです。`ResultResponse`クラスの `request_id`フィールドがデフォルトで`''`（空の文字列）で初期化されているため、`None`が代入された場合でも、空の文字列がデフォルト値として設定されます。変数宣言でデフォルト値として空の文字列`''`指定すると、その変数は常に空の文字列を持つことになるからです。

`Optional`は、Pythonの`typing`モジュールに含まれる型ヒントで、`None`を許容します（ということを知らなかったので少し苦戦した）。

`request_id: str = ''`の場合、デフォルト値として空の文字列`''`が指定されているため、初期化時に明示的に値を指定しなかった場合、`request_id`は空の文字列となってしまいます。これが`none is not an allowed value`というエラーが出た理由だったんですね。

## 公式ドキュメント
[Welcome to Pydantic - Pydantic](https://docs.pydantic.dev/latest/)