LoginSignup
1
1

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

Posted at

概要

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

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

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が発生します。

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

補足:データモデルでフィールド定義

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

サンプルコードでは、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)

変更した点は、データモデルResultResponserequest_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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1