見出し画像

Pydantic V2: Essentials: フィールドエイリアス (セクション4-1/13)

  • Python と JSON 間のフィールド名の違い(snake_case vs. camelCase など)をエイリアス設定で統一・管理できる。

  • `Field(alias=...)` や `serialization_alias=...`、さらには `alias_generator` を使うことで柔軟なデシリアライズ・シリアライズを実現できる。

  • `populate_by_name=True` を設定するとフィールド名とエイリアス両方からの入力に対応でき、予約語(`id` など)は `id_` のように工夫して衝突を避けられる。

Python で API を構築し、JSON を扱う際、同じフィールドに異なる名前が必要になることはよくあります。たとえば、JSON データは camelCase で来るのに対し、Python コードでは snake_case が想定されている場合や、`id` のような予約語をモデルのフィールドとして使いたくない場合です。Pydantic V2 は、こういった命名の不一致を処理するための強力な エイリアス の仕組みを提供します。また、外部から受け取るデータ(デシリアライズ)と、自分の API が出力するデータ(シリアライズ)で名前を分けることも可能です。

以下では、エイリアスの定義方法やデフォルト値との組み合わせ、エイリアスの自動生成、さらにフィールド名かエイリアスかを使う設定など、Pydantic でのエイリアス利用を詳細に見ていきます。


なぜエイリアスが必要なのか

  1. 異なる規約(Python と JSON)
    Python では変数や属性名に `snake_case`(例: `first_name`, `last_name`)を使うのが一般的です。しかし、多くの JSON ベース API では `camelCase`(例: `firstName`, `lastName`)が好まれます。いずれかに合わせるとどちらかが不自然になりがちですが、Pydantic のエイリアス機能を使えば、Python 側は snake_case のまま保ちつつ、JSON は camelCase にすることができます。

  2. 予約語
    Python には `id` や `list`, `filter` など組み込みで使われる単語があります。クラス属性名としてこれらを使うと、少なくともリンタが警告を出したり、紛らわしくなったりします。そこでモデル側では `id_` のようにアンダースコアを付けた名前を使い、JSON の入出力では `id` のままにするといった回避策が必要です。

  3. 外部と内部での一貫性
    外部から受け取るデータが不規則で、奇妙な大文字小文字ルールを持っていたり(例: `"ID"` や `"lastname"`)、社内標準と異なることがよくあります。モデル内部では整理された名前(`id_`, `last_name` など)を使いたい一方で、出力時にはさらに別の整った形式(`"id"`, `"lastName"`)で返したい場合があります。


`Field` を使ったフィールドエイリアスの定義

Pydantic の `Field` オブジェクトは、モデルのフィールドに追加設定を行うための直接的な手段です。たとえば、次のように書く代わりに:

class Person(BaseModel):
    id_: int
    last_name: str

エイリアスを指定するときは以下のようにします:

from pydantic import BaseModel, Field

class Person(BaseModel):
    id_: int = Field(alias="id")
    last_name: str = Field(alias="lastName")
  • デシリアライズ: エイリアス (`"id"`, `"lastName"`) を外部データで探し、Python 側では `id_`, `last_name` として扱います。

  • シリアライズ: デフォルトでは、Pydantic は Python のフィールド名(`id_`, `last_name`)をそのまま使用します(後述の方法でエイリアスにすることも可能)。

属性へのアクセス

Python 内部では、依然としてクラスの属性は `id_`, `last_name` であり、エイリアス名(`"id"`, `"lastName"`)は有効な属性名とはなりません:

p = Person(id=100, lastName="Gauss")
print(p.id_, p.last_name)  # 100 Gauss
hasattr(p, "lastName")     # False

`Field` とデフォルト値

デフォルト値も設定したい場合は、`Field(...)` のなかに指定します:

class Example(BaseModel):
    id_: int = Field(alias="id", default=100)
    description: str = Field(alias="desc", default="N/A")

ここでは、`desc` を省略してもデフォルトの `"N/A"` が適用されます。


シリアライズを制御する: `model_dump(by_alias=True)`

通常、`model_dump()` は Python 上のフィールド名を使って辞書を返します:

m = Person(id=1, lastName="Newton")
print(m.model_dump())
# {'id_': 1, 'last_name': 'Newton'}

もしエイリアスを使った辞書がほしければ、次のようにします:

m.model_dump(by_alias=True)
# {'id': 1, 'lastName': 'Newton'}

JSON にシリアライズするとき(`model_dump_json(by_alias=True)`)でも同様にエイリアスが反映されます。


`populate_by_name` でフィールド名とエイリアスの両方を受け入れる

通常、あるフィールドにエイリアスを定義した場合、入力 データはそのエイリアスを使わないと認識されません。フィールド名(`id_` など)でデータを渡してもエラーになります。これを変更するのが以下のオプションです:

class Model(BaseModel):
    model_config = ConfigDict(populate_by_name=True)
    id_: int = Field(alias="id")
    first_name: str = Field(alias="firstName")

こうしておくと、外部データがエイリアス (`{"id": 10}`) でもフィールド名 (`{"id_": 10}`) でも、どちらでも受理されるようになります。データソースが複数あって名前が混ざるときには便利です。


デシリアライズ用とシリアライズ用で別々のエイリアス

ときには、受信するとき送信するとき でまったく違う名前を使いたいことがあります。たとえば、外部 API が `"ID"`, `"FirstName"` というキーで返してくるが、自分の API は `"id"`, `"firstName"` で返したい場合などです。

class Person(BaseModel):
    id_: int = Field(alias="ID", serialization_alias="id")
    first_name: str = Field(alias="FirstName", serialization_alias="firstName")
    last_name: str = Field(alias="lastname", serialization_alias="lastName")
  • デシリアライズエイリアス (`alias="ID"`, `"FirstName"`, `"lastname"`) は入力に使われる。

  • シリアライズエイリアス (`serialization_alias="id"`, など) は `by_alias=True` でシリアライズするときに使われる。

これで、外部からは `"ID"` → `id_` → 内部に保持 → シリアライズ時には `"id"` と返す、といった動作が可能になります。

: `alias_generator`(下記参照)のように自動で シリアライズエイリアス を生成する仕組みは、現状 Pydantic にはありません。各フィールドに `serialization_alias=...` を個別指定する必要があります。


`alias_generator` を使ったエイリアスの自動生成

多数のフィールドで個別にエイリアスを定義するのは大変です。Pydantic では `alias_generator` を設定すれば、Python 側のフィールド名に対して自動的にエイリアスを生成できます。

たとえば、すべてを大文字にしたキーを入力として受け取りたい場合:

def make_upper(s: str) -> str:
    return s.upper()

class Person(BaseModel):
    model_config = ConfigDict(alias_generator=make_upper)
    id_: int
    first_name: str | None = None
    last_name: str

こうすると、`id_` なら `"ID_"`、`first_name` なら `"FIRST_NAME"`、`last_name` なら `"LAST_NAME"` というエイリアスが自動生成されます。デシリアライズする側はこれらの大文字キーを使わなければなりません。もし特定フィールドだけは別のエイリアスにしたい場合、`Field(alias="...")` を明示的に書けばそちらが優先されます。

Camel Case の場合

Pydantic には `to_camel`, `to_snake`, `to_pascal` という変換関数が用意されています。よくある snake_case → camelCase の変換には `to_camel` を使います:

from pydantic.alias_generators import to_camel

class Person(BaseModel):
    model_config = ConfigDict(alias_generator=to_camel)
    id_: int
    first_name: str
    last_name: str

`"first_name"` は `"firstName"` に変換される一方、`id_` はそのままだと `"id_"` になってしまうので、必要に応じて:

id_: int = Field(alias="id")

のように手動でオーバーライドします。


まとめ

ここまで、エイリアスとそれにまつわる便利な設定をいくつも紹介しました。最後に多くの機能を組み合わせた例を示します:

from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_camel

class Person(BaseModel):
    model_config = ConfigDict(
        alias_generator=to_camel,
        populate_by_name=True,   # エイリアスとフィールド名どちらでも入力OK
        extra="forbid"           # 未知のフィールドはエラーにする
    )
    id_: int = Field(alias="id", default=1)  # manual override
    first_name: str | None = None
    last_name: str
    age: int | None = None

# Python 風のフィールド名と camelCase エイリアスを混在
p = Person(id_=10, first_name="Isaac", lastName="Newton", age=84)
print(p)
# Person(id_=10, first_name='Isaac', last_name='Newton', age=84)

# JSON でも同様
data_json = """
{
    "id": 10,
    "firstName": "Isaac",
    "last_name": "Newton",
    "age": 84
}
"""
loaded = Person.model_validate_json(data_json)
print(loaded)
# Person(id_=10, first_name='Isaac', last_name='Newton', age=84)

# シリアライズ(エイリアスなし vs. エイリアスあり)
print(loaded.model_dump())
# {'id_': 10, 'first_name': 'Isaac', 'last_name': 'Newton', 'age': 84}

print(loaded.model_dump(by_alias=True))
# {'id': 10, 'firstName': 'Isaac', 'lastName': 'Newton', 'age': 84}

ポイントまとめ

  1. Plain Alias(通常の alias)

    • フィールドの `alias="..."` で宣言

    • デシリアライズで使われる(ただし validation alias があればそちらが優先)

    • シリアライズで `model_dump(by_alias=True)` を呼ぶときも、`serialization_alias` がなければこの alias が使われる

  2. Serialization Alias

    • `serialization_alias="..."` で宣言

    • `by_alias=True` でシリアライズするときにのみ使用

    • 入力データには影響せず、出力(API応答など)専用

  3. `populate_by_name=True`

    • モデル設定 `model_config` 内で指定

    • デシリアライズ時にエイリアスだけでなくフィールド名でも入力を受け付ける

  4. 自動エイリアス (`alias_generator`)

    • モデルレベルで関数をセットすると、各フィールド名を関数に通してエイリアスが決まる

    • 個別のフィールドで `Field(alias="...")` と書けば上書き可能

  5. `Field` によるデフォルト + エイリアス

    • エイリアスを設定しつつデフォルト値を与えるには `Field(alias="id", default=100)` のように一括で指定

  6. 予約語

    • `id` や `list` といった名前は Python では紛らわしいので `id_`, `list_` とするのが一般的

    • JSON のキーとしては `id`, `list` のままで、Pydantic 側のエイリアスを活用する

こうした機能を組み合わせることで、外部サービスからの JSON と自分の Python コードの間で煩雑なフィールド名をすっきり整理し、可読性を保ちながらデータを流通させることができます。Pydantic のエイリアス設定を上手く使えば、外部フォーマットをそのまま受け取りつつ、Python では整然とした型付きモデルを活用し、さらには自分の API からはより望ましいフォーマットで出力することが可能になります。

「超温和なパイソン」へ

いいなと思ったら応援しよう!