[{"data":1,"prerenderedAt":99},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Fpydantic\u002Fserialization":3},{"page":4,"siblings":86,"blog":78},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":77,"related":78,"seo":79,"seoDescription":80,"stem":81,"subtopic":6,"topic":82,"topicSlug":83,"updated":84,"__hash__":85},"qa\u002Ffastapi\u002Fpydantic\u002Fserialization.md","Serialization",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,3,"\u002Ffastapi\u002Fpydantic\u002Fserialization",[23,28,32,36,40,45,49,53,57,61,65,69,73],{"id":24,"difficulty":25,"q":26,"a":27},"model-dump-basics","easy","How do you convert a Pydantic model to a Python dict?","Call `.model_dump()` (Pydantic v2). It replaces the v1 `.dict()` method.\n\n```python\nfrom pydantic import BaseModel\n\nclass Item(BaseModel):\n    name: str\n    price: float\n    in_stock: bool = True\n\nitem = Item(name=\"Widget\", price=9.99)\nitem.model_dump()\n# {\"name\": \"Widget\", \"price\": 9.99, \"in_stock\": True}\n```\n\nKey keyword arguments:\n- `exclude_none=True` — drop `None` values\n- `exclude_unset=True` — drop fields not explicitly set\n- `exclude={\"field\"}` — drop specific fields\n- `include={\"field\"}` — keep only specific fields\n- `mode=\"json\"` — convert to JSON-safe types (datetime → str, UUID → str)\n\nRule of thumb: use `model_dump(mode=\"json\")` before storing to NoSQL or\npassing to `JSONResponse(content=...)` — it handles non-serialisable types.\n",{"id":29,"difficulty":25,"q":30,"a":31},"model-dump-json","What does `model_dump_json()` do and when should you use it over `model_dump()`?","`.model_dump_json()` serialises directly to a **JSON string** (bytes) without\ngoing through a Python dict intermediate. It's faster because Pydantic uses its\nRust core to serialise.\n\n```python\nitem = Item(name=\"Widget\", price=9.99)\nitem.model_dump_json()\n# b'{\"name\":\"Widget\",\"price\":9.99,\"in_stock\":true}'\n\n# round-trip: parse from JSON string\nItem.model_validate_json('{\"name\":\"Widget\",\"price\":9.99}')\n```\n\nUse `model_dump_json()` when writing to a cache, message queue, or file\nwhere you need a JSON string directly.\n\nRule of thumb: use `model_dump()` when you need a Python dict (to merge, modify,\nor pass to another dict); use `model_dump_json()` when you need a string\u002Fbytes.\n",{"id":33,"difficulty":14,"q":34,"a":35},"serialisation-alias","How do you use aliases during serialisation (output) in Pydantic v2?","By default, aliases affect **input** (parsing) only. To use aliases during\noutput (serialisation), pass `by_alias=True` to `model_dump()`:\n\n```python\nfrom pydantic import BaseModel, Field\n\nclass Order(BaseModel):\n    order_id: int = Field(alias=\"orderId\")\n    item_count: int = Field(alias=\"itemCount\")\n\norder = Order.model_validate({\"orderId\": 42, \"itemCount\": 3})\n\norder.model_dump()              # {\"order_id\": 42, \"item_count\": 3}  (Python names)\norder.model_dump(by_alias=True) # {\"orderId\": 42, \"itemCount\": 3}   (aliases)\n```\n\nIn FastAPI, set `response_model_by_alias=True` in the decorator to use aliases\nin the HTTP response:\n```python\n@app.get(\"\u002Forders\u002F{id}\", response_model=Order, response_model_by_alias=True)\n```\n\nRule of thumb: if your API contract uses camelCase, set `by_alias=True` globally\nin serialisation; don't mix Python names and aliases in the same API response.\n",{"id":37,"difficulty":14,"q":38,"a":39},"field-serializer","How do you customise how a specific field is serialised with `@field_serializer`?","Use `@field_serializer(\"field_name\")` to override the default serialisation for\na single field:\n\n```python\nfrom pydantic import BaseModel, field_serializer\nfrom datetime import datetime\n\nclass Event(BaseModel):\n    name: str\n    created_at: datetime\n\n    @field_serializer(\"created_at\")\n    def serialise_dt(self, v: datetime) -> str:\n        return v.strftime(\"%Y-%m-%d %H:%M\")   # custom format instead of ISO 8601\n\ne = Event(name=\"Launch\", created_at=datetime(2026, 6, 20, 9, 0))\ne.model_dump()\n# {\"name\": \"Launch\", \"created_at\": \"2026-06-20 09:00\"}\n```\n\nRule of thumb: use `@field_serializer` when you need a non-default format for\ndates, decimals, or custom types; prefer standard ISO 8601 datetimes unless\nthe client explicitly requires a different format.\n",{"id":41,"difficulty":42,"q":43,"a":44},"model-serializer","hard","What is `@model_serializer` and when would you use it?","`@model_serializer` replaces the entire default serialisation of a model with\na custom function. The function receives `self` (the model) and must return a\nJSON-serialisable value.\n\n```python\nfrom pydantic import BaseModel, model_serializer\n\nclass Money(BaseModel):\n    amount: int     # stored in cents\n    currency: str\n\n    @model_serializer\n    def to_dict(self):\n        return {\n            \"display\": f\"{self.amount \u002F 100:.2f} {self.currency}\",\n            \"cents\": self.amount,\n        }\n\nm = Money(amount=999, currency=\"USD\")\nm.model_dump()\n# {\"display\": \"9.99 USD\", \"cents\": 999}\n```\n\nRule of thumb: use `@model_serializer` only when the serialised shape differs\nfundamentally from the model's fields (e.g., presenting a monetary value as\ndisplay text + machine value).\n",{"id":46,"difficulty":14,"q":47,"a":48},"exclude-unset-serialisation","Why is `exclude_unset=True` important for PATCH endpoints?","`exclude_unset=True` returns only the fields the client **explicitly sent**,\nskipping fields that were left at their defaults. This is the correct behaviour\nfor a PATCH — you only update what was provided.\n\n```python\nclass ItemUpdate(BaseModel):\n    name: str | None = None\n    price: float | None = None\n    in_stock: bool | None = None\n\n@app.patch(\"\u002Fitems\u002F{id}\")\nasync def patch_item(id: int, patch: ItemUpdate):\n    updates = patch.model_dump(exclude_unset=True)\n    # client sent {\"price\": 14.99}\n    # updates = {\"price\": 14.99}  — name and in_stock NOT included\n    await db.update(id, updates)\n    return await db.get(id)\n```\n\nWithout `exclude_unset=True`, `updates` would include `{\"name\": None, \"price\": 14.99,\n\"in_stock\": None}`, accidentally overwriting existing values with `None`.\n\nRule of thumb: always use `model_dump(exclude_unset=True)` in PATCH handlers —\nit prevents accidental nulling of fields the client didn't mention.\n",{"id":50,"difficulty":14,"q":51,"a":52},"json-encoders","How do you configure Pydantic to encode custom types (like Decimal or UUID) to JSON?","In Pydantic v2 with `mode=\"json\"` or `model_dump_json()`, standard types like\n`datetime`, `UUID`, `Decimal`, and `Enum` are handled automatically.\n\nFor custom third-party types, use `@field_serializer`:\n\n```python\nfrom decimal import Decimal\nfrom pydantic import BaseModel, field_serializer\n\nclass Price(BaseModel):\n    amount: Decimal\n\n    @field_serializer(\"amount\")\n    def encode_decimal(self, v: Decimal) -> str:\n        return str(v)   # \"9.99\" instead of Decimal(\"9.99\")\n\nPrice(amount=Decimal(\"9.99\")).model_dump(mode=\"json\")\n# {\"amount\": \"9.99\"}\n```\n\nRule of thumb: run `model_dump(mode=\"json\")` in tests to check that all fields\nare JSON-serialisable — catch `TypeError` early before it surfaces in production.\n",{"id":54,"difficulty":14,"q":55,"a":56},"response-model-serialisation-flow","What is the exact serialisation flow when FastAPI returns a Pydantic model?","1. Handler returns a Python value (dict, ORM object, or Pydantic model).\n2. FastAPI calls `response_model.model_validate(value)` to filter and coerce it.\n3. The validated Pydantic instance is passed to `jsonable_encoder()` which calls\n   `model.model_dump(mode=\"json\")` internally.\n4. The resulting JSON-safe dict is serialised to bytes with `json.dumps()`\n   (or `orjson.dumps()` if `ORJSONResponse` is configured).\n5. Bytes are sent as `Content-Type: application\u002Fjson`.\n\n```python\n# simplified internal equivalent\nvalidated = ResponseModel.model_validate(handler_return)\npayload   = jsonable_encoder(validated)   # → JSON-safe dict\nbody      = json.dumps(payload).encode()   # → bytes\n```\n\nRule of thumb: understanding this flow explains why `response_model` filters\nextra fields (step 2) and why datetime values arrive as ISO strings (step 3).\n",{"id":58,"difficulty":14,"q":59,"a":60},"nested-serialisation","How does Pydantic handle serialisation of nested models?","Nested `BaseModel` instances are serialised recursively. `.model_dump()` returns\nnested dicts; `.model_dump_json()` returns a flat JSON string.\n\n```python\nclass Address(BaseModel):\n    city: str\n    country: str\n\nclass User(BaseModel):\n    name: str\n    address: Address\n\nu = User(name=\"Alice\", address=Address(city=\"London\", country=\"UK\"))\nu.model_dump()\n# {\"name\": \"Alice\", \"address\": {\"city\": \"London\", \"country\": \"UK\"}}\n```\n\n`exclude` and `include` work recursively:\n```python\nu.model_dump(exclude={\"address\": {\"country\"}})\n# {\"name\": \"Alice\", \"address\": {\"city\": \"London\"}}\n```\n\nRule of thumb: test serialisation of nested models explicitly — a missing\n`from_attributes=True` on an inner model is a common bug when using ORM objects.\n",{"id":62,"difficulty":25,"q":63,"a":64},"list-serialisation","How do you serialise a list of Pydantic models to JSON?","Call `.model_dump()` on each element, or use a `RootModel` for a top-level list:\n\n```python\nitems = [Item(name=\"A\", price=1.0), Item(name=\"B\", price=2.0)]\n[i.model_dump() for i in items]\n# [{\"name\": \"A\", \"price\": 1.0}, {\"name\": \"B\", \"price\": 2.0}]\n```\n\nIn FastAPI, returning a `list[Item]` from a handler with `response_model=list[Item]`\nhandles this automatically.\n\nFor a root-level list model:\n```python\nfrom pydantic import RootModel\n\nclass ItemList(RootModel[list[Item]]):\n    pass\n\nItemList([Item(name=\"A\", price=1.0)]).model_dump()\n# [{\"name\": \"A\", \"price\": 1.0}]\n```\n\nRule of thumb: let FastAPI handle list serialisation via `response_model=list[MyModel]`;\nuse `RootModel` only when you need to attach methods or validators to the list itself.\n",{"id":66,"difficulty":14,"q":67,"a":68},"model-copy-update","How do you create a modified copy of a Pydantic model without mutating the original?","Use `.model_copy(update={...})` (v2 replacement for `.copy(update=...)`):\n\n```python\noriginal = Item(name=\"Widget\", price=9.99, in_stock=True)\nupdated = original.model_copy(update={\"price\": 14.99})\n\nprint(original.price)  # 9.99   — unchanged\nprint(updated.price)   # 14.99  — new copy\n```\n\nThis is useful in PATCH handlers after merging the update dict with the existing\nDB row's values:\n\n```python\ndb_item = await db.get(id)\npydantic_item = ItemOut.model_validate(db_item)\nmerged = pydantic_item.model_copy(update=patch.model_dump(exclude_unset=True))\n```\n\nRule of thumb: use `model_copy(update=...)` instead of mutating model attributes\ndirectly — it keeps models immutable and makes the data flow explicit.\n",{"id":70,"difficulty":25,"q":71,"a":72},"serialise-enum","How does Pydantic serialise Python Enum values?","By default, Pydantic serialises an `Enum` to its `.value`. String enums\n(`str, Enum`) serialise as plain strings; int enums as integers.\n\n```python\nfrom enum import Enum\nfrom pydantic import BaseModel\n\nclass Status(str, Enum):\n    active = \"active\"\n    inactive = \"inactive\"\n\nclass User(BaseModel):\n    status: Status\n\nUser(status=Status.active).model_dump()\n# {\"status\": \"active\"}   — the string value, not the Enum object\n```\n\nIf you need the enum member name instead of value:\n```python\nuser.model_dump(mode=\"python\")   # {\"status\": \u003CStatus.active: 'active'>}\n```\n\nRule of thumb: always inherit from `str` (or `int`) when defining enums for\nPydantic models — it ensures JSON-safe serialisation without extra config.\n",{"id":74,"difficulty":42,"q":75,"a":76},"custom-json-schema","How do you customise the JSON Schema generated for a Pydantic model?","Use `json_schema_extra` in `model_config` to add or override schema properties:\n\n```python\nfrom pydantic import BaseModel, ConfigDict\n\nclass Item(BaseModel):\n    model_config = ConfigDict(\n        json_schema_extra={\n            \"title\": \"Inventory Item\",\n            \"examples\": [\n                {\"name\": \"Widget\", \"price\": 9.99}\n            ],\n        }\n    )\n    name: str\n    price: float\n```\n\nFor programmatic customisation (add\u002Fremove properties):\n```python\nmodel_config = ConfigDict(\n    json_schema_extra=lambda schema: schema.update({\"deprecated\": True})\n)\n```\n\nRule of thumb: use `json_schema_extra` to add `examples` and `title` to your\nmodels — Swagger UI renders examples in the \"Try it out\" body, saving testers time.\n",13,null,{"description":11},"FastAPI Pydantic serialization interview questions — model_dump, model_dump_json, aliases, computed fields, custom serializers and JSON encoding.","fastapi\u002Fpydantic\u002Fserialization","Pydantic & Validation","pydantic","2026-06-20","4U5BqhZb56t9jr1i-1r3zHccnOkJicu8N1PgKoi4I-A",[87,91,94,95],{"subtopic":88,"path":89,"order":90},"Pydantic Models","\u002Ffastapi\u002Fpydantic\u002Fmodels",1,{"subtopic":92,"path":93,"order":12},"Validators","\u002Ffastapi\u002Fpydantic\u002Fvalidation",{"subtopic":6,"path":21,"order":20},{"subtopic":96,"path":97,"order":98},"Settings Management","\u002Ffastapi\u002Fpydantic\u002Fsettings",4,1782244112854]