[{"data":1,"prerenderedAt":297},["ShallowReactive",2],{"topic-fastapi-pydantic":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Ffastapi.yml","FastAPI interview questions on async routing, Pydantic validation, dependency injection, OAuth2 security, database integration and deployment — the go-to Python framework for production APIs.","yml","fastapi",{},"FastAPI",6,"frameworks\u002Ffastapi",1,"lgr_X74wBdBYovbrlazGWPWqqi-YwNEUq44l1BmtgyE",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Ffastapi-pydantic.yml","BaseModel, field validators, serialization and BaseSettings — Pydantic v2 powers FastAPI's entire request validation and config story.",{},"Pydantic & Validation",3,"pydantic","topics\u002Ffastapi-pydantic","bQyImPYZKMyV5ma4Y1CZQ0j3Ub80FRXzGDRn6ofwBek",[25,103,167,233],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":95,"related":96,"seo":97,"seoDescription":98,"stem":99,"subtopic":100,"topic":19,"topicSlug":21,"updated":101,"__hash__":102},"qa\u002Ffastapi\u002Fpydantic\u002Fmodels.md","Models",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Ffastapi\u002Fpydantic\u002Fmodels",[41,46,50,54,58,62,66,70,74,78,82,86,90],{"id":42,"difficulty":43,"q":44,"a":45},"basemodel-basics","easy","What is Pydantic's `BaseModel` and why does FastAPI rely on it?","`BaseModel` is Pydantic's foundation class. Subclasses declare fields as\nclass-level annotations; Pydantic validates and coerces incoming data at\ninstantiation time.\n\n```python\nfrom pydantic import BaseModel\n\nclass Item(BaseModel):\n    name: str\n    price: float\n    quantity: int = 1    # default value\n\nitem = Item(name=\"Widget\", price=9.99)\n# Item(name='Widget', price=9.99, quantity=1)\n\nItem(name=\"Widget\", price=\"nine\")   # raises ValidationError: price must be float\n```\n\nFastAPI uses `BaseModel` to:\n1. Parse and validate incoming JSON bodies.\n2. Serialise outgoing responses.\n3. Generate OpenAPI JSON Schema.\n\nRule of thumb: use a `BaseModel` subclass for any structured data that crosses\nthe HTTP boundary — it's free validation and documentation.\n",{"id":47,"difficulty":43,"q":48,"a":49},"field-defaults","How do you add metadata (description, example, constraints) to a Pydantic field?","Use `Field()` from Pydantic:\n\n```python\nfrom pydantic import BaseModel, Field\n\nclass Product(BaseModel):\n    name: str = Field(min_length=1, max_length=100, description=\"Product name\")\n    price: float = Field(gt=0, description=\"Price in USD\", example=9.99)\n    sku: str = Field(pattern=r\"^[A-Z]{3}-\\d{4}$\", examples=[\"ABC-1234\"])\n```\n\n`Field()` parameters:\n- Constraints: `gt`, `ge`, `lt`, `le`, `min_length`, `max_length`, `pattern`, `multiple_of`\n- Metadata: `title`, `description`, `example`, `examples`\n- Behaviour: `default`, `default_factory`, `alias`, `exclude`\n\nRule of thumb: use `Field()` for any field that needs a constraint or a helpful\ndescription — it ends up in the OpenAPI schema and saves clients guessing.\n",{"id":51,"difficulty":43,"q":52,"a":53},"required-vs-optional","How do you mark a Pydantic field as required vs optional?","- **Required**: no default value — Pydantic raises `ValidationError` if omitted.\n- **Optional with None**: `field: T | None = None` — accepts `None` or the type.\n- **Optional with default**: `field: T = default_value`.\n\n```python\nfrom pydantic import BaseModel\n\nclass User(BaseModel):\n    id: int                      # required\n    name: str                    # required\n    bio: str | None = None       # optional, defaults to None\n    role: str = \"viewer\"         # optional, defaults to \"viewer\"\n```\n\nIn OpenAPI:\n- Required fields → `required: [...]` array in the schema.\n- Optional fields → absent from `required`, with `default` or `nullable`.\n\nRule of thumb: think of `None` default as \"not provided\"; use a semantic default\nlike `\"viewer\"` when the field has a meaningful fallback value.\n",{"id":55,"difficulty":35,"q":56,"a":57},"field-alias","What is a field alias in Pydantic and when would you use it?","An alias lets the JSON key differ from the Python attribute name. Useful when\nthe API uses camelCase, hyphens, or reserved Python keywords.\n\n```python\nfrom pydantic import BaseModel, Field\n\nclass Order(BaseModel):\n    order_id: int = Field(alias=\"orderId\")        # JSON: \"orderId\"\n    item_count: int = Field(alias=\"itemCount\")\n\n# Parsing from JSON (camelCase input)\norder = Order.model_validate({\"orderId\": 42, \"itemCount\": 3})\nprint(order.order_id)  # 42   (Python snake_case attribute)\n```\n\nFor global camelCase ↔ snake_case conversion use `model_config`:\n```python\nfrom pydantic import ConfigDict\n\nclass MyModel(BaseModel):\n    model_config = ConfigDict(alias_generator=lambda s: s.replace(\"_\", \"\"), populate_by_name=True)\n    order_id: int\n```\n\nRule of thumb: use `alias` for a few fields; use `alias_generator` when the\nentire API uses camelCase (common with JavaScript clients).\n",{"id":59,"difficulty":35,"q":60,"a":61},"model-inheritance","How does Pydantic model inheritance work and what are its use cases in FastAPI?","A child model inherits all fields from its parent and can add or override them.\n\n```python\nclass ItemBase(BaseModel):\n    name: str\n    price: float\n\nclass ItemCreate(ItemBase):\n    # used for POST body — no id yet\n    pass\n\nclass ItemUpdate(ItemBase):\n    # all fields optional for PATCH\n    name: str | None = None\n    price: float | None = None\n\nclass ItemOut(ItemBase):\n    id: int            # added by DB\n    created_at: datetime\n```\n\nThis pattern keeps field definitions DRY while giving each use-case the exact\nshape it needs.\n\nRule of thumb: define a `Base` model with shared fields, then derive `Create`,\n`Update`, and `Out` variants — the \"Input\u002FOutput DTO\" pattern.\n",{"id":63,"difficulty":35,"q":64,"a":65},"model-config","What is `model_config` in Pydantic v2 and what can you configure with it?","`model_config = ConfigDict(...)` replaces Pydantic v1's inner `class Config`.\nKey options:\n\n| Setting | Effect |\n|---------|--------|\n| `extra=\"forbid\"` | Reject extra fields |\n| `frozen=True` | Make instances immutable (hashable) |\n| `populate_by_name=True` | Allow both alias and name |\n| `from_attributes=True` | Parse from ORM objects (SQLAlchemy) |\n| `str_strip_whitespace=True` | Auto-strip leading\u002Ftrailing spaces |\n| `alias_generator=fn` | Auto-generate aliases for all fields |\n\n```python\nfrom pydantic import BaseModel, ConfigDict\n\nclass StrictUser(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\", frozen=True)\n    id: int\n    name: str\n```\n\nRule of thumb: set `from_attributes=True` on any model that reads from SQLAlchemy\nORM objects; set `extra=\"forbid\"` on request models to catch client typos.\n",{"id":67,"difficulty":35,"q":68,"a":69},"orm-mode","What is `from_attributes=True` (formerly `orm_mode`) and why is it needed?","By default Pydantic models only parse from dicts. `from_attributes=True` allows\nparsing from any object with attributes — including SQLAlchemy ORM instances.\n\n```python\nfrom pydantic import BaseModel, ConfigDict\nfrom sqlalchemy import Column, Integer, String\nfrom sqlalchemy.orm import DeclarativeBase\n\nclass Base(DeclarativeBase): pass\n\nclass UserORM(Base):\n    __tablename__ = \"users\"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n\nclass UserOut(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n    id: int\n    name: str\n\nuser_orm = session.get(UserORM, 1)\nuser_out = UserOut.model_validate(user_orm)   # reads .id and .name attributes\n```\n\nWithout `from_attributes=True`, `model_validate(orm_obj)` raises a\n`ValidationError` because ORM objects aren't dicts.\n\nRule of thumb: always set `from_attributes=True` on output schemas that will\nbe constructed from SQLAlchemy models.\n",{"id":71,"difficulty":35,"q":72,"a":73},"computed-field","How do you add a computed (read-only) field to a Pydantic model?","Use `@computed_field` (Pydantic v2):\n\n```python\nfrom pydantic import BaseModel, computed_field\n\nclass Rectangle(BaseModel):\n    width: float\n    height: float\n\n    @computed_field\n    @property\n    def area(self) -> float:\n        return self.width * self.height\n\nr = Rectangle(width=3.0, height=4.0)\nprint(r.area)           # 12.0\nprint(r.model_dump())   # {\"width\": 3.0, \"height\": 4.0, \"area\": 12.0}\n```\n\n`@computed_field` fields are included in serialisation and the OpenAPI schema.\nThey are always read-only — you can't set them from input.\n\nRule of thumb: use `@computed_field` for derived values that belong in the\nresponse (full name from first + last, URL from ID); avoid heavy computation in them.\n",{"id":75,"difficulty":43,"q":76,"a":77},"model-validate","What is `model_validate()` and how does it differ from calling the constructor?","`Model.model_validate(obj)` is the explicit way to parse data in Pydantic v2.\nIt accepts a dict **or** any object (with `from_attributes=True`) and returns a\nvalidated model instance.\n\n```python\n# constructor — same as model_validate for dicts\nitem = Item(name=\"Widget\", price=9.99)\n\n# model_validate — more explicit, required for non-dict input\nitem = Item.model_validate({\"name\": \"Widget\", \"price\": 9.99})\nitem = Item.model_validate(orm_instance)   # needs from_attributes=True\n```\n\nIn FastAPI, `model_validate` is called internally when parsing request bodies.\nYou call it explicitly when converting ORM objects to Pydantic models in service\nor repository layers.\n\nRule of thumb: use the constructor for tests with literal dicts; use\n`model_validate` in production code where the source object might be an ORM row.\n",{"id":79,"difficulty":43,"q":80,"a":81},"model-dump","What does `model_dump()` return and what options does it accept?","`model_dump()` returns a Python dict of the model's fields. It's Pydantic v2's\nreplacement for `.dict()`.\n\n```python\nitem = Item(name=\"Widget\", price=9.99)\nitem.model_dump()\n# {\"name\": \"Widget\", \"price\": 9.99}\n\n# exclude fields\nitem.model_dump(exclude={\"price\"})\n# {\"name\": \"Widget\"}\n\n# only unset fields (for PATCH)\nitem.model_dump(exclude_unset=True)\n\n# JSON-safe output (datetime → str, UUID → str)\nitem.model_dump(mode=\"json\")\n```\n\nRule of thumb: use `model_dump(exclude_unset=True)` in PATCH handlers to get\nonly the fields the client explicitly sent.\n",{"id":83,"difficulty":35,"q":84,"a":85},"model-json-schema","How do you get the JSON Schema for a Pydantic model?","Call `Model.model_json_schema()`:\n\n```python\nimport json\nfrom pydantic import BaseModel, Field\n\nclass Item(BaseModel):\n    name: str = Field(min_length=1)\n    price: float = Field(gt=0)\n\nprint(json.dumps(Item.model_json_schema(), indent=2))\n# {\n#   \"title\": \"Item\",\n#   \"type\": \"object\",\n#   \"properties\": {\n#     \"name\": {\"type\": \"string\", \"minLength\": 1},\n#     \"price\": {\"type\": \"number\", \"exclusiveMinimum\": 0}\n#   },\n#   \"required\": [\"name\", \"price\"]\n# }\n```\n\nFastAPI embeds this schema in `\u002Fopenapi.json` automatically. You might call\n`model_json_schema()` directly to validate schemas in tests or export them to\nother systems.\n\nRule of thumb: write a test that calls `model_json_schema()` and asserts key\nproperties — it catches breaking schema changes before they hit production.\n",{"id":87,"difficulty":35,"q":88,"a":89},"pydantic-v1-vs-v2","What are the key differences between Pydantic v1 and v2 that affect FastAPI code?","| Feature | Pydantic v1 | Pydantic v2 |\n|---------|-------------|-------------|\n| Config | `class Config:` | `model_config = ConfigDict(...)` |\n| ORM mode | `orm_mode = True` | `from_attributes=True` |\n| Serialise | `.dict()` \u002F `.json()` | `.model_dump()` \u002F `.model_dump_json()` |\n| Parse | `MyModel(**data)` \u002F `.parse_obj()` | `.model_validate(data)` |\n| Validators | `@validator` | `@field_validator` \u002F `@model_validator` |\n| Performance | Pure Python | Rust core (10-50× faster) |\n\nFastAPI 0.100+ requires Pydantic v2. Code targeting both versions uses the\n`pydantic.v1` compatibility shim.\n\nRule of thumb: always use Pydantic v2 APIs in new code; if maintaining a\nv1 codebase, migrate validators first (they have the most breaking changes).\n",{"id":91,"difficulty":92,"q":93,"a":94},"discriminated-union","hard","What is a discriminated union in Pydantic and when is it useful in FastAPI?","A discriminated union uses a `Literal` field as a **type tag** to unambiguously\nselect which model to use during parsing, instead of trying each model in order.\n\n```python\nfrom typing import Literal, Union, Annotated\nfrom pydantic import BaseModel, Field\n\nclass Cat(BaseModel):\n    type: Literal[\"cat\"]\n    meows: bool\n\nclass Dog(BaseModel):\n    type: Literal[\"dog\"]\n    barks: bool\n\nclass PetPayload(BaseModel):\n    pet: Annotated[Union[Cat, Dog], Field(discriminator=\"type\")]\n\npayload = PetPayload.model_validate({\"pet\": {\"type\": \"dog\", \"barks\": True}})\nprint(type(payload.pet))  # \u003Cclass 'Dog'>\n```\n\nDiscriminated unions are:\n- **Faster** — no trial-and-error parsing.\n- **Clearer errors** — \"expected type to be 'cat' or 'dog'\" vs generic failure.\n- **Better OpenAPI** — generates `oneOf` with a discriminator property.\n\nRule of thumb: whenever a body can be one of several shapes, add a `type` tag\nand use a discriminated union — it's explicit, fast, and self-documenting.\n",13,null,{"description":32},"FastAPI Pydantic model interview questions — BaseModel, Field, model_config, nested models, inheritance, computed fields and ORM mode.","fastapi\u002Fpydantic\u002Fmodels","Pydantic Models","2026-06-20","Aj-1OWK5V5w5kHw0JCExAUHqEZCCOYZA0iqfVhNFxzw",{"id":104,"title":105,"body":106,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":110,"navigation":38,"order":33,"path":111,"questions":112,"questionsCount":161,"related":96,"seo":162,"seoDescription":163,"stem":164,"subtopic":165,"topic":19,"topicSlug":21,"updated":101,"__hash__":166},"qa\u002Ffastapi\u002Fpydantic\u002Fvalidation.md","Validation",{"type":29,"value":107,"toc":108},[],{"title":32,"searchDepth":33,"depth":33,"links":109},[],{},"\u002Ffastapi\u002Fpydantic\u002Fvalidation",[113,117,121,125,129,133,137,141,145,149,153,157],{"id":114,"difficulty":35,"q":115,"a":116},"field-validator-basics","How do you write a custom field validator in Pydantic v2?","Use `@field_validator(\"field_name\")` decorator on a classmethod:\n\n```python\nfrom pydantic import BaseModel, field_validator\n\nclass User(BaseModel):\n    username: str\n    age: int\n\n    @field_validator(\"username\")\n    @classmethod\n    def username_must_be_alphanumeric(cls, v: str) -> str:\n        if not v.isalnum():\n            raise ValueError(\"username must be alphanumeric\")\n        return v.lower()   # transform the value\n\n    @field_validator(\"age\")\n    @classmethod\n    def age_must_be_positive(cls, v: int) -> int:\n        if v \u003C 0:\n            raise ValueError(\"age must be non-negative\")\n        return v\n```\n\n`ValueError` messages are caught by Pydantic and included in the `ValidationError`\ndetail. You can also raise `PydanticCustomError` for more structured errors.\n\nRule of thumb: field validators run per-field; use them for constraints that\ncan't be expressed with `Field()` parameters (cross-value logic belongs in\nmodel validators).\n",{"id":118,"difficulty":35,"q":119,"a":120},"field-validator-modes","What are the `before`, `after`, `wrap` and `plain` modes for `@field_validator`?","`mode` controls when the validator runs relative to Pydantic's built-in type coercion:\n\n| Mode | Runs | Input type | Use case |\n|------|------|------------|----------|\n| `\"before\"` (default v2) | before coercion | raw input (str\u002Fdict\u002Fetc.) | normalize raw strings |\n| `\"after\"` | after coercion | declared Python type | validate typed value |\n| `\"wrap\"` | wraps coercion | raw + handler callable | conditional coercion |\n| `\"plain\"` | replaces coercion | raw input | fully custom type parsing |\n\n```python\nfrom pydantic import BaseModel, field_validator\n\nclass Order(BaseModel):\n    amount: float\n\n    @field_validator(\"amount\", mode=\"before\")\n    @classmethod\n    def strip_currency_symbol(cls, v):\n        if isinstance(v, str):\n            return v.lstrip(\"$£€\")   # \"£9.99\" → \"9.99\"\n        return v\n```\n\nRule of thumb: use `mode=\"before\"` to normalise raw strings before Pydantic\ntries to parse them; use `mode=\"after\"` for logic that needs the typed value.\n",{"id":122,"difficulty":35,"q":123,"a":124},"multiple-fields-validator","How do you validate multiple fields with a single `@field_validator`?","Pass multiple field names to `@field_validator`. The validator is called once\nper field listed:\n\n```python\nfrom pydantic import BaseModel, field_validator\n\nclass Product(BaseModel):\n    name: str\n    description: str\n\n    @field_validator(\"name\", \"description\")\n    @classmethod\n    def must_not_be_blank(cls, v: str) -> str:\n        if not v.strip():\n            raise ValueError(\"must not be blank or whitespace\")\n        return v.strip()\n```\n\nThe validator receives one field value at a time — it does not receive both\nsimultaneously. For cross-field logic, use `@model_validator`.\n\nRule of thumb: share validators across fields with multiple names in the\ndecorator to keep DRY; use `@model_validator` when the logic depends on comparing\nfield values to each other.\n",{"id":126,"difficulty":35,"q":127,"a":128},"model-validator","What is `@model_validator` and when do you need it instead of `@field_validator`?","`@model_validator` runs once on the **whole model** (all fields at once). Use it\nfor cross-field constraints.\n\n```python\nfrom pydantic import BaseModel, model_validator\n\nclass DateRange(BaseModel):\n    start: date\n    end: date\n\n    @model_validator(mode=\"after\")\n    def end_after_start(self) -> \"DateRange\":\n        if self.end \u003C= self.start:\n            raise ValueError(\"end must be after start\")\n        return self\n```\n\n`mode=\"after\"` — runs after all fields are coerced and validated; `self` is the\nmodel instance.\n`mode=\"before\"` — runs before field validation; receives raw data as a dict.\n\nRule of thumb: `@field_validator` for single-field logic; `@model_validator(mode=\"after\")`\nfor cross-field constraints like \"end after start\" or \"confirm password matches\".\n",{"id":130,"difficulty":92,"q":131,"a":132},"model-validator-before","When would you use `@model_validator(mode=\"before\")` and what does it receive?","`mode=\"before\"` intercepts the raw input **before any field parsing**. The\nvalidator receives a dict (or arbitrary input) and must return a dict.\n\n```python\nfrom pydantic import BaseModel, model_validator\n\nclass FlexibleUser(BaseModel):\n    name: str\n    email: str\n\n    @model_validator(mode=\"before\")\n    @classmethod\n    def coerce_legacy_format(cls, data):\n        # support both {\"name\": ...} and {\"full_name\": ...}\n        if \"full_name\" in data and \"name\" not in data:\n            data[\"name\"] = data.pop(\"full_name\")\n        return data\n```\n\nUse cases: renaming legacy fields, setting computed defaults before field\nvalidation, accepting multiple input shapes.\n\nRule of thumb: `mode=\"before\"` is for input normalisation at the whole-model\nlevel; prefer `mode=\"after\"` for validation because you get typed values.\n",{"id":134,"difficulty":92,"q":135,"a":136},"custom-type","How do you create a custom Pydantic type with its own validation logic?","Annotate a class with `__get_validators__` (v1) or implement `__get_pydantic_core_schema__`\n(v2). For simple cases, use `Annotated` with `AfterValidator`:\n\n```python\nfrom typing import Annotated\nfrom pydantic import AfterValidator, BaseModel\n\ndef validate_isbn(v: str) -> str:\n    v = v.replace(\"-\", \"\")\n    if len(v) not in (10, 13):\n        raise ValueError(\"ISBN must be 10 or 13 digits\")\n    return v\n\nISBN = Annotated[str, AfterValidator(validate_isbn)]\n\nclass Book(BaseModel):\n    title: str\n    isbn: ISBN\n```\n\nFor full custom types with JSON Schema:\n```python\nfrom pydantic import GetCoreSchemaHandler\nfrom pydantic_core import core_schema\n\nclass PositiveDecimal:\n    @classmethod\n    def __get_pydantic_core_schema__(cls, source, handler: GetCoreSchemaHandler):\n        return core_schema.no_info_after_validator_function(cls, core_schema.decimal_schema())\n```\n\nRule of thumb: use `Annotated[T, AfterValidator(fn)]` for simple reusable\nvalidators; implement `__get_pydantic_core_schema__` only for complex custom types.\n",{"id":138,"difficulty":35,"q":139,"a":140},"pydantic-error-structure","What does a `ValidationError` from Pydantic look like and how do you access its details?","`ValidationError` has an `.errors()` method returning a list of error dicts:\n\n```python\nfrom pydantic import BaseModel, ValidationError\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\ntry:\n    Item(name=\"\", price=-1)\nexcept ValidationError as e:\n    print(e.errors())\n# [\n#   {\"type\": \"value_error\", \"loc\": (\"name\",), \"msg\": \"...\", \"input\": \"\"},\n#   {\"type\": \"greater_than\", \"loc\": (\"price\",), \"msg\": \"...\", \"input\": -1}\n# ]\n```\n\nEach error has:\n- `type` — Pydantic error code\n- `loc` — tuple of keys pinpointing the failing field\n- `msg` — human-readable message\n- `input` — the value that failed\n\nFastAPI captures this and returns it as the 422 response `detail` array.\n\nRule of thumb: in unit tests, assert on `exc.errors()[0][\"type\"]` to test\nspecific error codes rather than message strings that may change.\n",{"id":142,"difficulty":92,"q":143,"a":144},"pydantic-strict-mode","What is strict mode in Pydantic v2 and when would you enable it?","Strict mode disables Pydantic's **type coercion** — values must already be the\ncorrect Python type; no implicit conversion happens.\n\n```python\nfrom pydantic import BaseModel, ConfigDict\n\nclass StrictItem(BaseModel):\n    model_config = ConfigDict(strict=True)\n    price: float\n    count: int\n\nStrictItem(price=9.99, count=3)     # OK\nStrictItem(price=\"9.99\", count=3)   # ValidationError — \"9.99\" is str, not float\n```\n\nPer-field: `Field(strict=True)` or `Strict` annotated type.\n\nUse cases: data coming from other Python code (not raw JSON) where silent coercion\nhides bugs; internal domain objects that must have exact types.\n\nRule of thumb: don't use strict mode for API request models — clients send JSON\nwhere numbers may arrive as strings; use it for internal\u002FDTO models between Python layers.\n",{"id":146,"difficulty":35,"q":147,"a":148},"validator-skip-on-none","How do you skip a validator when the field value is `None`?","Pass `skip_on_failure=True` (for `mode=\"after\"`) or check for `None` at the top\nof the validator. With `mode=\"before\"` and optional fields, validators run even\non `None` inputs by default.\n\n```python\nfrom pydantic import BaseModel, field_validator\n\nclass Profile(BaseModel):\n    bio: str | None = None\n\n    @field_validator(\"bio\", mode=\"after\")\n    @classmethod\n    def bio_max_sentences(cls, v: str | None) -> str | None:\n        if v is None:\n            return v    # skip check\n        if v.count(\".\") > 5:\n            raise ValueError(\"bio may not exceed 5 sentences\")\n        return v\n```\n\nAlternatively, in Pydantic v2 you can annotate with `Optional` inside\n`Annotated` and chain with `BeforeValidator` that returns `None` early.\n\nRule of thumb: always guard against `None` at the top of validators for\noptional fields — a missed check causes a confusing `AttributeError`.\n",{"id":150,"difficulty":43,"q":151,"a":152},"validator-return-value","Must a Pydantic field validator return a value?","Yes — the return value of a `@field_validator` replaces the field's value.\nForgetting to `return v` silently sets the field to `None`.\n\n```python\n# WRONG — returns None, field becomes None\n@field_validator(\"name\")\n@classmethod\ndef check_name(cls, v: str) -> str:\n    if not v:\n        raise ValueError(\"required\")\n    # forgot return v!\n\n# CORRECT\n@field_validator(\"name\")\n@classmethod\ndef check_name(cls, v: str) -> str:\n    if not v:\n        raise ValueError(\"required\")\n    return v   # must return the (possibly transformed) value\n```\n\nRule of thumb: end every `@field_validator` with a `return v` — validators\nthat only validate (no transform) still must return the original value.\n",{"id":154,"difficulty":35,"q":155,"a":156},"before-validator-annotated","What is `BeforeValidator` \u002F `AfterValidator` in Pydantic v2 `Annotated` style?","These are functional wrappers used inside `Annotated` to attach validators\nwithout subclassing `BaseModel`:\n\n```python\nfrom typing import Annotated\nfrom pydantic import BaseModel, BeforeValidator, AfterValidator\n\ndef strip_spaces(v: str) -> str:\n    return v.strip()\n\ndef ensure_lower(v: str) -> str:\n    return v.lower()\n\nCleanStr = Annotated[str, BeforeValidator(strip_spaces), AfterValidator(ensure_lower)]\n\nclass User(BaseModel):\n    username: CleanStr\n```\n\nThis creates **reusable annotated types** you can import and use across models\nwithout copy-pasting validators.\n\nRule of thumb: define common transformations as `Annotated` types (`Email`,\n`SlugStr`, `PositiveDecimal`) and import them — it's DRY and keeps validators\nout of model classes.\n",{"id":158,"difficulty":92,"q":159,"a":160},"cross-model-validation","How do you validate that two related request models are consistent with each other in FastAPI?","Accept both models as body parameters and validate their relationship in the\nhandler (or in a dedicated service function):\n\n```python\nclass DateRangeFilter(BaseModel):\n    start: date\n    end: date\n\n    @model_validator(mode=\"after\")\n    def end_after_start(self) -> \"DateRangeFilter\":\n        if self.end \u003C self.start:\n            raise ValueError(\"end must be >= start\")\n        return self\n\nclass ReportRequest(BaseModel):\n    range: DateRangeFilter\n    metrics: list[str]\n\n@app.post(\"\u002Freports\")\nasync def generate_report(req: ReportRequest):\n    ...\n```\n\nFor cross-model checks that span the body and path\u002Fquery params, perform the\ncheck in the handler and raise `HTTPException(422, ...)`.\n\nRule of thumb: encode single-model invariants in `@model_validator`; encode\ncross-model or cross-layer invariants in the handler or service layer.\n",12,{"description":32},"FastAPI Pydantic validator interview questions — field_validator, model_validator, before\u002Fafter mode, custom types and raising ValidationError.","fastapi\u002Fpydantic\u002Fvalidation","Validators","EXg87mVabg1_159V7NR7LEHuU_pfzrq0kolsiqv3NcI",{"id":168,"title":169,"body":170,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":174,"navigation":38,"order":20,"path":175,"questions":176,"questionsCount":95,"related":96,"seo":229,"seoDescription":230,"stem":231,"subtopic":169,"topic":19,"topicSlug":21,"updated":101,"__hash__":232},"qa\u002Ffastapi\u002Fpydantic\u002Fserialization.md","Serialization",{"type":29,"value":171,"toc":172},[],{"title":32,"searchDepth":33,"depth":33,"links":173},[],{},"\u002Ffastapi\u002Fpydantic\u002Fserialization",[177,181,185,189,193,197,201,205,209,213,217,221,225],{"id":178,"difficulty":43,"q":179,"a":180},"model-dump-basics","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":182,"difficulty":43,"q":183,"a":184},"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":186,"difficulty":35,"q":187,"a":188},"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":190,"difficulty":35,"q":191,"a":192},"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":194,"difficulty":92,"q":195,"a":196},"model-serializer","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":198,"difficulty":35,"q":199,"a":200},"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":202,"difficulty":35,"q":203,"a":204},"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":206,"difficulty":35,"q":207,"a":208},"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":210,"difficulty":35,"q":211,"a":212},"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":214,"difficulty":43,"q":215,"a":216},"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":218,"difficulty":35,"q":219,"a":220},"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":222,"difficulty":43,"q":223,"a":224},"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":226,"difficulty":92,"q":227,"a":228},"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",{"description":32},"FastAPI Pydantic serialization interview questions — model_dump, model_dump_json, aliases, computed fields, custom serializers and JSON encoding.","fastapi\u002Fpydantic\u002Fserialization","4U5BqhZb56t9jr1i-1r3zHccnOkJicu8N1PgKoi4I-A",{"id":234,"title":235,"body":236,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":240,"navigation":38,"order":241,"path":242,"questions":243,"questionsCount":161,"related":96,"seo":292,"seoDescription":293,"stem":294,"subtopic":295,"topic":19,"topicSlug":21,"updated":101,"__hash__":296},"qa\u002Ffastapi\u002Fpydantic\u002Fsettings.md","Settings",{"type":29,"value":237,"toc":238},[],{"title":32,"searchDepth":33,"depth":33,"links":239},[],{},4,"\u002Ffastapi\u002Fpydantic\u002Fsettings",[244,248,252,256,260,264,268,272,276,280,284,288],{"id":245,"difficulty":43,"q":246,"a":247},"basesettings-basics","What is `BaseSettings` and why use it for FastAPI configuration?","`BaseSettings` (from `pydantic-settings`) is a `BaseModel` subclass that reads\nfield values from **environment variables** automatically. It gives you typed,\nvalidated configuration without manual `os.getenv()` calls.\n\n```python\nfrom pydantic_settings import BaseSettings\n\nclass Settings(BaseSettings):\n    database_url: str\n    secret_key: str\n    debug: bool = False\n    max_connections: int = 10\n\nsettings = Settings()\n# reads DATABASE_URL, SECRET_KEY, DEBUG, MAX_CONNECTIONS from env\n```\n\nBenefits over raw `os.getenv()`:\n- Pydantic validates types and raises a clear error at startup if required vars are missing.\n- Settings are documented as a class — easy to audit.\n- Works with `.env` files, Docker secrets, and AWS Parameter Store.\n\nRule of thumb: put all configuration in a `BaseSettings` class; never scatter\n`os.getenv()` calls across the codebase.\n",{"id":249,"difficulty":43,"q":250,"a":251},"env-file","How do you load configuration from a `.env` file with `BaseSettings`?","Configure `model_config` with `env_file`:\n\n```python\nfrom pydantic_settings import BaseSettings\n\nclass Settings(BaseSettings):\n    model_config = {\"env_file\": \".env\", \"env_file_encoding\": \"utf-8\"}\n\n    database_url: str\n    secret_key: str\n    debug: bool = False\n```\n\n`.env` file:\n```\nDATABASE_URL=postgresql+asyncpg:\u002F\u002Fuser:pass@localhost\u002Fmydb\nSECRET_KEY=supersecret\nDEBUG=true\n```\n\nMultiple `.env` files (later overrides earlier):\n```python\nmodel_config = {\"env_file\": (\".env\", \".env.local\")}\n```\n\nRule of thumb: commit `.env.example` with dummy values and add `.env` to\n`.gitignore` — never commit real secrets to version control.\n",{"id":253,"difficulty":35,"q":254,"a":255},"lru-cache-settings","How do you avoid re-reading environment variables on every request in FastAPI?","Wrap the `Settings()` constructor in `@lru_cache` and inject it via `Depends`:\n\n```python\nfrom functools import lru_cache\nfrom fastapi import Depends\n\n@lru_cache\ndef get_settings() -> Settings:\n    return Settings()   # reads env\u002Ffile once; cached for process lifetime\n\n@app.get(\"\u002Finfo\")\nasync def info(settings: Settings = Depends(get_settings)):\n    return {\"debug\": settings.debug}\n```\n\n`@lru_cache` makes `get_settings()` a singleton: the first call creates the\nobject; subsequent calls return the same instance. Safe because environment\nvariables don't change during a process lifetime.\n\nRule of thumb: always wrap settings construction in `@lru_cache` — reading env\nvars is cheap, but parsing and validating with Pydantic on every request adds\nunnecessary overhead.\n",{"id":257,"difficulty":43,"q":258,"a":259},"settings-case-sensitivity","Are `BaseSettings` environment variable names case-sensitive?","By default **case-insensitive** on all platforms. `DATABASE_URL`,\n`database_url`, and `Database_Url` all resolve to the `database_url` field.\n\n```python\nclass Settings(BaseSettings):\n    database_url: str   # matches DATABASE_URL, database_url, etc.\n```\n\nTo enforce case-sensitive env var names:\n```python\nmodel_config = {\"case_sensitive\": True}\n```\n\nRule of thumb: use UPPERCASE for environment variable names by convention\n(Linux, 12-factor apps); keep field names lowercase — the case-insensitive\nmatching bridges them automatically.\n",{"id":261,"difficulty":35,"q":262,"a":263},"nested-settings","How do you organise complex settings into nested groups with `BaseSettings`?","Use nested Pydantic models. `BaseSettings` reads nested values via a delimiter\nprefix in the env var name:\n\n```python\nfrom pydantic import BaseModel\nfrom pydantic_settings import BaseSettings\n\nclass DatabaseSettings(BaseModel):\n    url: str\n    pool_size: int = 5\n\nclass Settings(BaseSettings):\n    model_config = {\"env_nested_delimiter\": \"__\"}\n    database: DatabaseSettings\n    debug: bool = False\n\n# env vars: DATABASE__URL=postgres:\u002F\u002F..., DATABASE__POOL_SIZE=10\n```\n\nRule of thumb: use `env_nested_delimiter=\"__\"` for nested settings — it's the\nconventional double-underscore pattern in 12-factor apps.\n",{"id":265,"difficulty":35,"q":266,"a":267},"secrets-directory","How do you load secrets from files (e.g., Docker secrets) with `BaseSettings`?","Point `secrets_dir` to the directory where secret files live. Each file named\nafter the env var contains the secret value.\n\n```python\nfrom pydantic_settings import BaseSettings\n\nclass Settings(BaseSettings):\n    model_config = {\"secrets_dir\": \"\u002Frun\u002Fsecrets\"}\n    database_password: str   # reads \u002Frun\u002Fsecrets\u002Fdatabase_password\n    api_key: str             # reads \u002Frun\u002Fsecrets\u002Fapi_key\n```\n\nDocker Swarm and Kubernetes both mount secrets as files at a known path.\nThis approach keeps secrets out of environment variables (less visible in\n`ps` output and container inspection).\n\nRule of thumb: prefer secrets-as-files over env var secrets in containerised\ndeployments — they integrate cleanly with Kubernetes Secrets and Docker secrets.\n",{"id":269,"difficulty":35,"q":270,"a":271},"settings-override-in-tests","How do you override settings values in tests?","Override the `get_settings` dependency on the test app:\n\n```python\nfrom fastapi.testclient import TestClient\nfrom app.main import app\nfrom app.config import get_settings, Settings\n\ndef get_test_settings():\n    return Settings(\n        database_url=\"sqlite:\u002F\u002F\u002F:memory:\",\n        secret_key=\"test-secret\",\n        debug=True,\n    )\n\napp.dependency_overrides[get_settings] = get_test_settings\nclient = TestClient(app)\n```\n\nAlternatively, set environment variables before the settings are loaded:\n```python\nimport os\nos.environ[\"DATABASE_URL\"] = \"sqlite:\u002F\u002F\u002F:memory:\"\n```\n\nRule of thumb: use `dependency_overrides` — it's explicit, isolated per test\nfile, and doesn't pollute `os.environ` for other tests.\n",{"id":273,"difficulty":35,"q":274,"a":275},"settings-validators","Can you add validators to `BaseSettings` fields?","Yes — `BaseSettings` inherits from `BaseModel`, so `@field_validator` and\n`@model_validator` work identically:\n\n```python\nfrom pydantic import field_validator\nfrom pydantic_settings import BaseSettings\n\nclass Settings(BaseSettings):\n    allowed_hosts: list[str] = [\"*\"]\n    cors_origins: str = \"\"\n\n    @field_validator(\"cors_origins\", mode=\"before\")\n    @classmethod\n    def parse_cors(cls, v: str) -> list[str]:\n        return [origin.strip() for origin in v.split(\",\") if origin.strip()]\n```\n\nThis pattern lets you store a comma-separated env var and parse it into a list.\n\nRule of thumb: use `@field_validator(mode=\"before\")` in `BaseSettings` to parse\ncompound env vars (comma-separated lists, JSON strings) into the target type.\n",{"id":277,"difficulty":35,"q":278,"a":279},"settings-singleton-pattern","What is the singleton pattern for settings in FastAPI and what is the risk of using a module-level instance?","A **module-level singleton**:\n```python\n# config.py\nsettings = Settings()   # created once at import time\n```\n\nThis works in production but breaks in tests: if `os.environ` is patched after\nimport, the singleton already holds the old values.\n\nThe **`Depends` + `@lru_cache` pattern** is safer:\n```python\n@lru_cache\ndef get_settings() -> Settings:\n    return Settings()\n\n# In tests:\napp.dependency_overrides[get_settings] = lambda: Settings(debug=True)\n```\n\nThe `lru_cache` singleton is invalidated between tests by clearing the cache:\n```python\nget_settings.cache_clear()\n```\n\nRule of thumb: use `Depends(get_settings)` with `@lru_cache` for testability;\navoid module-level `settings = Settings()` in anything you'll need to test.\n",{"id":281,"difficulty":43,"q":282,"a":283},"settings-field-types","What Python types work well with `BaseSettings` for environment variables?","All Pydantic-supported types work. Pydantic coerces the string value from the\nenv var to the declared type:\n\n| Python type | Env var example |\n|-------------|-----------------|\n| `str` | `SECRET_KEY=abc` |\n| `int` | `PORT=8000` |\n| `float` | `TIMEOUT=30.5` |\n| `bool` | `DEBUG=true` \u002F `DEBUG=1` |\n| `list[str]` | `TAGS=[\"a\",\"b\"]` (JSON) or use `@field_validator` |\n| `HttpUrl` | `BASE_URL=https:\u002F\u002Fexample.com` |\n| `SecretStr` | `PASSWORD=secret` (masked in repr) |\n\n```python\nfrom pydantic import SecretStr, HttpUrl\n\nclass Settings(BaseSettings):\n    database_password: SecretStr    # hidden in logs\n    api_base_url: HttpUrl           # validated URL\n```\n\nRule of thumb: use `SecretStr` for passwords and tokens — it masks the value in\n`repr()` and `str()`, preventing accidental logging.\n",{"id":285,"difficulty":43,"q":286,"a":287},"settings-multiple-envs","How do you support different settings for development, staging and production?","Load different `.env` files based on an `APP_ENV` env var:\n\n```python\nimport os\nfrom pydantic_settings import BaseSettings\n\nclass Settings(BaseSettings):\n    model_config = {\n        \"env_file\": f\".env.{os.getenv('APP_ENV', 'development')}\"\n    }\n    database_url: str\n    debug: bool = False\n```\n\nFile layout:\n```\n.env.development   → local dev DB, DEBUG=true\n.env.staging       → staging DB, DEBUG=false\n.env.production    → prod secrets (not committed)\n```\n\nIn CI\u002FCD and production, inject all values directly as environment variables\nrather than relying on files.\n\nRule of thumb: in production, always prefer environment variables over `.env`\nfiles — files can be accidentally committed or left on disk.\n",{"id":289,"difficulty":43,"q":290,"a":291},"pydantic-settings-install","Is `BaseSettings` included in Pydantic itself and what do you need to install?","Since Pydantic v2, `BaseSettings` has been **moved to a separate package**:\n`pydantic-settings`. It must be installed separately:\n\n```bash\npip install pydantic-settings\n```\n\nImport:\n```python\nfrom pydantic_settings import BaseSettings\n```\n\nIn Pydantic v1, `BaseSettings` was part of `pydantic` itself\n(`from pydantic import BaseSettings`) — a common migration mistake is forgetting\nto install `pydantic-settings` after upgrading.\n\nRule of thumb: add `pydantic-settings` to `requirements.txt` or `pyproject.toml`\nexplicitly — it is not pulled in by `fastapi` or `pydantic` alone.\n",{"description":32},"FastAPI Pydantic settings interview questions — BaseSettings, env vars, .env files, secrets, nested settings and caching with lru_cache.","fastapi\u002Fpydantic\u002Fsettings","Settings Management","qUIpbUnYHzlmYx-BO9rV1OP-iYYDvd_l2Q22HsoNIo8",1782244096211]