Backend / Web frameworks / Pydantic / 01_pydantic_interview.md

Pydantic — Common Interview Questions and Answers

Updated 3 interview angles 4 min read source
On this page16
  1. 1. What is Pydantic and what is it used for?
  2. 2. How do you define a basic Pydantic model?
  3. 3. What is the difference between Pydantic v1 and Pydantic v2?
  4. 4. How do you make a field optional and set a default?
  5. 5. What are validators and how do you add custom validation?
  6. 6. What is the difference between validator and field_validator in Pydantic v2?
  7. 7. How do you validate the whole model (e.g. cross-field validation)?
  8. 8. How do you serialize a Pydantic model to dict or JSON?
  9. 9. What are Pydantic Field and ConfigDict used for?
  10. 10. How do you handle nested models?
  11. 11. What is the difference between __init__ and model construction in Pydantic?
  12. 12. How do you allow or forbid extra fields?
  13. 13. What are Pydantic settings and how do you load them?
  14. 14. How do you use Pydantic with JSON/dict that uses different key names (aliases)?
  15. 15. How does FastAPI use Pydantic?
  16. Interview angle

Pydantic — Common Interview Questions and Answers

1. What is Pydantic and what is it used for?

Pydantic is a data validation and settings library that uses Python type annotations. It is used to:

  • Validate and parse input data (e.g. API request bodies, env vars)
  • Serialize data to JSON/dict
  • Enforce types and constraints at runtime
  • Generate JSON Schema (e.g. for OpenAPI in FastAPI)

2. How do you define a basic Pydantic model?

python
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool = True

Instances are created from dicts or keyword args; validation runs automatically.

3. What is the difference between Pydantic v1 and Pydantic v2?

  • v2 (Pydantic 2.x): Rewritten in Rust for speed; new validation decorators (@field_validator), model_config instead of Config class, different error format, BaseModel.model_validate() / .model_dump().
  • v1: validator decorators, inner Config class, .dict() / .parse_obj().

New projects should use v2.

4. How do you make a field optional and set a default?

python
from typing import Optional

class Item(BaseModel):
    name: str
    description: Optional[str] = None
    quantity: int = 0

Optional[str] = None allows None or missing; quantity: int = 0 gives a default value.

5. What are validators and how do you add custom validation?

Validators check or transform field values. In Pydantic v2 you use @field_validator or @model_validator:

python
from pydantic import BaseModel, field_validator

class User(BaseModel):
    email: str
    age: int

    @field_validator("email")
    @classmethod
    def email_must_contain_at(cls, v: str) -> str:
        if "@" not in v:
            raise ValueError("Invalid email")
        return v.lower()

    @field_validator("age")
    @classmethod
    def age_in_range(cls, v: int) -> int:
        if not 0 <= v <= 150:
            raise ValueError("Age must be 0-150")
        return v

6. What is the difference between validator and field_validator in Pydantic v2?

In v2, @field_validator is for single fields. @model_validator runs on the whole model (e.g. cross-field checks). The old @validator from v1 was replaced by these.

7. How do you validate the whole model (e.g. cross-field validation)?

Use @model_validator with mode='after' to work with the built model:

python
from pydantic import BaseModel, model_validator

class Range(BaseModel):
    start: int
    end: int

    @model_validator(mode="after")
    def start_before_end(self):
        if self.start >= self.end:
            raise ValueError("start must be less than end")
        return self

8. How do you serialize a Pydantic model to dict or JSON?

In Pydantic v2:

  • .model_dump() — model to dict (Python types)
  • .model_dump_json() — model to JSON string

You can use model_dump(exclude_none=True) or by_alias=True to match your API schema.

9. What are Pydantic Field and ConfigDict used for?

Field() adds metadata and constraints to a field:

python
from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    price: float = Field(gt=0, description="Price in USD")
    tags: list[str] = Field(default_factory=list, max_length=10)

ConfigDict (in v2) replaces the inner Config class for model-wide settings (e.g. str_strip_whitespace, validate_assignment, extra='forbid').

10. How do you handle nested models?

Use another Pydantic model as a type:

python
from pydantic import BaseModel

class Address(BaseModel):
    street: str
    city: str
    zip_code: str

class User(BaseModel):
    name: str
    address: Address

Nested models are validated and serialized recursively.

11. What is the difference between __init__ and model construction in Pydantic?

Pydantic models don’t rely on a hand-written __init__. The generated constructor:

  • Validates and coerces types
  • Applies validators and defaults
  • Handles Optional and None

You can still add custom __init__ in v2 with care (e.g. calling super().__init__(**data)), but usually default construction is enough.

12. How do you allow or forbid extra fields?

In v2, use model_config:

python
from pydantic import BaseModel, ConfigDict

class Strict(BaseModel):
    # no extra keys allowed
    model_config = ConfigDict(extra="forbid")

class AllowExtra(BaseModel):
    # extra keys stored
    model_config = ConfigDict(extra="allow")

extra="ignore" (default in many cases) ignores extra keys without storing them.

13. What are Pydantic settings and how do you load them?

BaseSettings (in pydantic_settings) loads config from env vars and .env files:

python
from pydantic_settings import (
    BaseSettings,
    SettingsConfigDict,
)

class Settings(BaseSettings):
    app_name: str = "My App"
    debug: bool = False
    database_url: str

    model_config = SettingsConfigDict(env_file=".env")

settings = Settings()

Use for app configuration and secrets (with care).

14. How do you use Pydantic with JSON/dict that uses different key names (aliases)?

Use Field(alias="...") or model_config with populate_by_name=True:

python
from pydantic import BaseModel, Field

class User(BaseModel):
    first_name: str = Field(alias="firstName")
    last_name: str = Field(alias="lastName")

Then User.model_validate({"firstName": "John", "lastName": "Doe"}) works. Use model_dump(by_alias=True) to serialize with alias keys.

15. How does FastAPI use Pydantic?

FastAPI uses Pydantic to:

  • Parse and validate request bodies (body → Pydantic model)
  • Validate query/path/header parameters when typed
  • Serialize response models to JSON
  • Generate OpenAPI schema from models and field metadata

Any BaseModel used as a body or response model is validated and documented automatically.

Interview angle 3

  • “What does Pydantic give you over a dataclass?” - runtime validation and coercion from the type annotations, plus serialisation, JSON Schema generation and rich error reporting. A dataclass annotates types but never checks them.
  • “What changed in v2?” - the validation core moved to Rust for a large speedup, and the API renamed: model_validate, model_dump, field_validator, model_config. Recognising the v1 names as deprecated matters when reading existing code.
  • “When is a dataclass the better choice?” - internal objects where the data is already trusted and validation is pure overhead. Validate at the boundary, then use plain types inside.