Skip to content

Generated Model API

Every generated _pydantic.py file contains a _ProtoModel base class that all message classes in that file inherit from. It overrides model_dump and model_dump_json to omit zero-value fields and use proto field names by default — matching ProtoJSON conventions — with no extra setup required.

_ProtoModel

class _ProtoModel(_BaseModel):
    """Base class for generated Pydantic models with ProtoJSON helpers."""

_ProtoModel is generated automatically. You never write or import it directly — you just use the message classes that inherit from it.

Default model_config

_ProtoModel sets several Pydantic config options to match proto3 / ProtoJSON semantics:

Config key Value Effect
use_enum_values True Enum values (strings or ints) are stored, not enum members
ser_json_bytes "base64" bytes fields serialize to Base64 in JSON
val_json_bytes "base64" bytes fields are decoded from Base64 when parsing JSON
ser_json_inf_nan "strings" inf / NaN float values serialize as strings, not JSON null

These defaults match proto3 JSON encoding rules so that model_dump_json() output is compatible with proto-aware consumers.

Serialization methods

model_dump(**kwargs) -> dict

Serialize to a dict using ProtoJSON conventions:

  • Omits fields at their default (zero) value (exclude_defaults=True)
  • Uses original proto field names (by_alias=True)
user = User(name="Alice", age=30, active=False)
user.model_dump()
# {'name': 'Alice', 'age': 30}  ← active omitted (False is the default)

You can override either default:

user.model_dump(exclude_defaults=False)  # include zero-value fields
user.model_dump(by_alias=False)  # use Python attribute names

model_dump_json(**kwargs) -> str

Serialize to a JSON string using ProtoJSON conventions:

  • Omits fields at their default (zero) value (exclude_defaults=True)
  • Uses original proto field names (by_alias=True)
user.model_dump_json()
# '{"name":"Alice","age":30}'

Deserialization

Use standard Pydantic class methods — no custom wrappers needed:

user = User.model_validate({"name": "Alice", "age": 30})
user = User.model_validate_json('{"name":"Alice","age":30}')

Models with reserved-name fields (e.g. bool_ aliased to "bool") also accept the original proto name in input data because populate_by_name=True is set on those models.

model_dump() vs plain Pydantic

Because _ProtoModel overrides model_dump and model_dump_json rather than adding custom methods, the ProtoJSON defaults also apply when a ProtoModel is nested inside another model — Pydantic calls model_dump internally when serializing nested objects, so the override is the only approach that works correctly in all cases.

Override the defaults by passing kwargs explicitly:

user.model_dump(exclude_defaults=False)  # include zero-value fields
user.model_dump(by_alias=False)  # use Python attribute names

_proto_types.py

Alongside each _pydantic.py file, the generator writes _proto_types.py. This file contains type aliases and helper functions used by the generated models. It is conditional — only helpers actually needed by the proto files in that directory are included.

gen/
└── api/v1/
    ├── user_pydantic.py
    ├── order_pydantic.py
    └── _proto_types.py        ← generated alongside model files

64-bit integer types

Proto3 encodes int64, sint64, sfixed64, uint64, and fixed64 as strings in JSON (to avoid JavaScript integer overflow). The generated aliases handle this automatically:

Type alias Proto types JSON representation
ProtoInt64 int64, sint64, sfixed64 "123" (string)
ProtoUInt64 uint64, fixed64 "123" (string)

Both are annotated int in Python — arithmetic works normally. The string serialization only applies in JSON:

from api.v1.scalars_pydantic import Scalars

s = Scalars(int64=9007199254740993)  # larger than JS MAX_SAFE_INTEGER
s.model_dump_json()
# '{"int64":"9007199254740993"}'  ← serialized as string
s.int64 + 1  # arithmetic works normally in Python → 9007199254740994

Timestamp and Duration

google.protobuf.Timestamp and google.protobuf.Duration map to Python's datetime.datetime and datetime.timedelta respectively, with proto-wire-format JSON serialization:

Type alias Python type JSON format
ProtoTimestamp datetime.datetime RFC 3339 / ISO 8601 with Z suffix
ProtoDuration datetime.timedelta "<seconds>s" string (e.g. "3600s")
import datetime
from api.v1.event_pydantic import Event

event = Event(
    occurred=datetime.datetime.now(datetime.timezone.utc),
    duration=datetime.timedelta(hours=1),
)
event.model_dump_json()
# '{"occurred":"2024-01-15T10:30:00Z","duration":"3600s"}'

ProtoTimestamp accepts both datetime objects and ISO 8601 strings (with Z suffix) as input, so you can parse directly from JSON payloads:

Event.model_validate_json('{"occurred":"2024-01-15T10:30:00Z","duration":"3600s"}')