[{"data":1,"prerenderedAt":1122},["ShallowReactive",2],{"nav":3,"page-\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002F":310,"surround-\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002F":1120},[4,96,212],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,30,42,54,66,90],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation and Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators and Field Constraints in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18,24],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex",[23],{"title":19,"path":20,"stem":21},{"title":25,"path":26,"stem":27,"children":28},"Pydantic v2 Async Custom Validator: What to Do Instead","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fpydantic-v2-async-custom-validator\u002Findex",[29],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"JSON Schema Customization in Pydantic and FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[35,36],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Customizing OpenAPI Schema Generation in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Fcustomizing-openapi-schema-generation-in-fastapi\u002Findex",[41],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[47,48],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Handling Deeply Nested JSON Models Efficiently","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Fhandling-deeply-nested-json-models-efficiently\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Performance Optimization for Pydantic Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"Pydantic Model Serialization Performance in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Fpydantic-model-serialization-performance\u002Findex",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Pydantic V2 Migration Guide for FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[71,72,78,84],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Migrate @validator to @field_validator in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex",[77],{"title":73,"path":74,"stem":75},{"title":79,"path":80,"stem":81,"children":82},"Migrating from Pydantic v1 to v2 Without Breaking APIs","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002Findex",[83],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87,"children":88},"model_config vs class Config in Pydantic v2","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002Findex",[89],{"title":85,"path":86,"stem":87},{"title":91,"path":92,"stem":93,"children":94},"Type Hinting and IDE Integration in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[95],{"title":91,"path":92,"stem":93},{"title":97,"path":98,"stem":99,"children":100},"Async Background Tasks Observability","\u002Fasync-background-tasks-observability","async-background-tasks-observability",[101,104,122,140,158,176,194],{"title":102,"path":98,"stem":103},"Async, Background Tasks, and Observability in FastAPI","async-background-tasks-observability\u002Findex",{"title":105,"path":106,"stem":107,"children":108},"Async Correctness and Concurrency in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Findex",[109,110,116],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"FastAPI async def vs def: Performance and When to Use Each","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffastapi-async-def-vs-def-performance\u002Findex",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Fixing Blocking Calls in Async FastAPI Routes","\u002Fasync-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes","async-background-tasks-observability\u002Fasync-correctness-concurrency\u002Ffixing-blocking-calls-in-async-routes\u002Findex",[121],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"Async Database Sessions in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions","async-background-tasks-observability\u002Fasync-database-sessions\u002Findex",[127,128,134],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Async SQLAlchemy Session per Request in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request","async-background-tasks-observability\u002Fasync-database-sessions\u002Fasync-sqlalchemy-session-per-request\u002Findex",[133],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Fixing asyncpg Connection Pool Exhaustion in FastAPI","\u002Fasync-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion","async-background-tasks-observability\u002Fasync-database-sessions\u002Ffixing-asyncpg-pool-exhaustion\u002Findex",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Background Task Processing in FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing","async-background-tasks-observability\u002Fbackground-task-processing\u002Findex",[145,146,152],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"FastAPI BackgroundTasks vs Celery vs ARQ","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq","async-background-tasks-observability\u002Fbackground-task-processing\u002Ffastapi-backgroundtasks-vs-celery-vs-arq\u002Findex",[151],{"title":147,"path":148,"stem":149},{"title":153,"path":154,"stem":155,"children":156},"Running ARQ Workers with FastAPI","\u002Fasync-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi","async-background-tasks-observability\u002Fbackground-task-processing\u002Frunning-arq-workers-with-fastapi\u002Findex",[157],{"title":153,"path":154,"stem":155},{"title":159,"path":160,"stem":161,"children":162},"Caching Strategies in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies","async-background-tasks-observability\u002Fcaching-strategies\u002Findex",[163,164,170],{"title":159,"path":160,"stem":161},{"title":165,"path":166,"stem":167,"children":168},"Cache Invalidation Patterns in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fcache-invalidation-patterns-in-fastapi\u002Findex",[169],{"title":165,"path":166,"stem":167},{"title":171,"path":172,"stem":173,"children":174},"Redis Response Caching in FastAPI","\u002Fasync-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi","async-background-tasks-observability\u002Fcaching-strategies\u002Fredis-response-caching-in-fastapi\u002Findex",[175],{"title":171,"path":172,"stem":173},{"title":177,"path":178,"stem":179,"children":180},"Observability and Tracing in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing","async-background-tasks-observability\u002Fobservability-and-tracing\u002Findex",[181,182,188],{"title":177,"path":178,"stem":179},{"title":183,"path":184,"stem":185,"children":186},"Instrumenting FastAPI with OpenTelemetry","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry","async-background-tasks-observability\u002Fobservability-and-tracing\u002Finstrumenting-fastapi-with-opentelemetry\u002Findex",[187],{"title":183,"path":184,"stem":185},{"title":189,"path":190,"stem":191,"children":192},"Structured JSON Logging with Request IDs in FastAPI","\u002Fasync-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids","async-background-tasks-observability\u002Fobservability-and-tracing\u002Fstructured-json-logging-with-request-ids\u002Findex",[193],{"title":189,"path":190,"stem":191},{"title":195,"path":196,"stem":197,"children":198},"Rate Limiting and Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Findex",[199,200,206],{"title":195,"path":196,"stem":197},{"title":201,"path":202,"stem":203,"children":204},"FastAPI Rate Limiting with Redis and SlowAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi","async-background-tasks-observability\u002Frate-limiting-throttling\u002Ffastapi-rate-limiting-with-redis-slowapi\u002Findex",[205],{"title":201,"path":202,"stem":203},{"title":207,"path":208,"stem":209,"children":210},"Per-User Token Bucket Throttling in FastAPI","\u002Fasync-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling","async-background-tasks-observability\u002Frate-limiting-throttling\u002Fper-user-token-bucket-throttling\u002Findex",[211],{"title":207,"path":208,"stem":209},{"title":213,"path":214,"stem":215,"children":216},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[217,220,232,250,268,280,292],{"title":218,"path":214,"stem":219},"FastAPI Core Architecture and Routing Patterns","core-architecture-routing-patterns\u002Findex",{"title":221,"path":222,"stem":223,"children":224},"Application Factory Patterns in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[225,226],{"title":221,"path":222,"stem":223},{"title":227,"path":228,"stem":229,"children":230},"FastAPI App Factory Pattern for Testing and Deployment","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Ffastapi-app-factory-pattern-for-testing-and-deployment\u002Findex",[231],{"title":227,"path":228,"stem":229},{"title":233,"path":234,"stem":235,"children":236},"Configuration Management in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[237,238,244],{"title":233,"path":234,"stem":235},{"title":239,"path":240,"stem":241,"children":242},"Managing Environment Variables with Pydantic Settings","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fmanaging-environment-variables-with-pydantic-settings\u002Findex",[243],{"title":239,"path":240,"stem":241},{"title":245,"path":246,"stem":247,"children":248},"Pydantic Settings vs Dynaconf vs python-decouple","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple","core-architecture-routing-patterns\u002Fconfiguration-management\u002Fpydantic-settings-vs-dynaconf-vs-python-decouple\u002Findex",[249],{"title":245,"path":246,"stem":247},{"title":251,"path":252,"stem":253,"children":254},"Dependency Injection Strategies in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[255,256,262],{"title":251,"path":252,"stem":253},{"title":257,"path":258,"stem":259,"children":260},"Best Practices for FastAPI Dependency Injection","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Fbest-practices-for-fastapi-dependency-injection\u002Findex",[261],{"title":257,"path":258,"stem":259},{"title":263,"path":264,"stem":265,"children":266},"Fixing FastAPI Dependency Injection Circular Imports","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Ffastapi-dependency-injection-circular-import-fix\u002Findex",[267],{"title":263,"path":264,"stem":265},{"title":269,"path":270,"stem":271,"children":272},"Error Handling and Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[273,274],{"title":269,"path":270,"stem":271},{"title":275,"path":276,"stem":277,"children":278},"Global Exception Handlers for Consistent API Responses","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Fglobal-exception-handlers-for-consistent-api-responses\u002Findex",[279],{"title":275,"path":276,"stem":277},{"title":281,"path":282,"stem":283,"children":284},"Middleware Implementation in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[285,286],{"title":281,"path":282,"stem":283},{"title":287,"path":288,"stem":289,"children":290},"Implementing Custom Middleware for Request Tracing","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Fimplementing-custom-middleware-for-request-tracing\u002Findex",[291],{"title":287,"path":288,"stem":289},{"title":293,"path":294,"stem":295,"children":296},"Modular Router Organization in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[297,298,304],{"title":293,"path":294,"stem":295},{"title":299,"path":300,"stem":301,"children":302},"APIRouter Prefix vs Sub-Application Mounting in FastAPI","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fapirouter-prefix-vs-sub-application-mounting\u002Findex",[303],{"title":299,"path":300,"stem":301},{"title":305,"path":306,"stem":307,"children":308},"How to Structure Large FastAPI Projects for Scale","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Fhow-to-structure-large-fastapi-projects-for-scale\u002Findex",[309],{"title":305,"path":306,"stem":307},{"id":311,"title":73,"body":312,"description":1073,"extension":1074,"meta":1075,"navigation":469,"path":74,"seo":1118,"stem":75,"__hash__":1119},"content\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002Findex.md",{"type":313,"value":314,"toc":1059},"minimark",[315,319,326,379,392,397,403,407,420,424,429,546,647,656,747,753,886,890,926,930,1029,1033,1055],[316,317,73],"h1",{"id":318},"migrate-validator-to-field_validator-in-pydantic-v2",[320,321,322],"p",{},[323,324,325],"strong",{},"Key takeaways:",[327,328,329,346,360,370,376],"ul",{},[330,331,332,333,337,338,341,342,345],"li",{},"Rename ",[334,335,336],"code",{},"@validator"," to ",[334,339,340],{},"@field_validator"," and add ",[334,343,344],{},"@classmethod"," beneath it.",[330,347,348,351,352,355,356,359],{},[334,349,350],{},"pre=True"," becomes ",[334,353,354],{},"mode=\"before\"","; the default is ",[334,357,358],{},"mode=\"after\"",".",[330,361,362,363,366,367,359],{},"Reading other fields moves from the ",[334,364,365],{},"values"," dict to a ",[334,368,369],{},"model_validator",[330,371,372,373,359],{},"Multiple field names still pass positionally to ",[334,374,375],{},"field_validator",[330,377,378],{},"Verify with the contract suite that behavior is unchanged.",[320,380,381,382,387,388,359],{},"This is a focused slice of the ",[383,384,386],"a",{"href":385},"\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002F","Pydantic V2 Migration Guide",", and it builds on ",[383,389,391],{"href":390},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002F","Custom Validators and Field Constraints",[393,394,396],"h2",{"id":395},"the-problem-this-solves","The Problem This Solves",[320,398,399,400,402],{},"The validator decorator is one of the most-used Pydantic features, so migrating it correctly is most of the work in a v1-to-v2 move. The renames are simple, but two semantic changes — the classmethod requirement and the loss of the ",[334,401,365],{}," dict — trip people up.",[393,404,406],{"id":405},"prerequisites","Prerequisites",[327,408,409,412],{},[330,410,411],{},"Pydantic v2 installed.",[330,413,414,415,419],{},"A contract test suite pinned before changes (see the ",[383,416,418],{"href":417},"\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrating-from-pydantic-v1-to-v2-without-breaking-apis\u002F","non-breaking migration guide",").",[393,421,423],{"id":422},"step-by-step-implementation","Step-by-Step Implementation",[425,426,428],"h3",{"id":427},"_1-single-field-validator","1. Single-field validator",[430,431,436],"pre",{"className":432,"code":433,"language":434,"meta":435,"style":435},"language-python shiki shiki-themes github-light","# Pydantic v1\nfrom pydantic import BaseModel, validator\n\n\nclass V1(BaseModel):\n    name: str\n\n    @validator(\"name\")\n    def strip(cls, v):\n        return v.strip()\n","python","",[334,437,438,447,464,471,476,495,505,510,525,537],{"__ignoreMap":435},[439,440,443],"span",{"class":441,"line":442},"line",1,[439,444,446],{"class":445},"sAwPA","# Pydantic v1\n",[439,448,450,454,458,461],{"class":441,"line":449},2,[439,451,453],{"class":452},"sD7c4","from",[439,455,457],{"class":456},"sgsFI"," pydantic ",[439,459,460],{"class":452},"import",[439,462,463],{"class":456}," BaseModel, validator\n",[439,465,467],{"class":441,"line":466},3,[439,468,470],{"emptyLinePlaceholder":469},true,"\n",[439,472,474],{"class":441,"line":473},4,[439,475,470],{"emptyLinePlaceholder":469},[439,477,479,482,486,489,492],{"class":441,"line":478},5,[439,480,481],{"class":452},"class",[439,483,485],{"class":484},"s7eDp"," V1",[439,487,488],{"class":456},"(",[439,490,491],{"class":484},"BaseModel",[439,493,494],{"class":456},"):\n",[439,496,498,501],{"class":441,"line":497},6,[439,499,500],{"class":456},"    name: ",[439,502,504],{"class":503},"sYu0t","str\n",[439,506,508],{"class":441,"line":507},7,[439,509,470],{"emptyLinePlaceholder":469},[439,511,513,516,518,522],{"class":441,"line":512},8,[439,514,515],{"class":484},"    @validator",[439,517,488],{"class":456},[439,519,521],{"class":520},"sYBdl","\"name\"",[439,523,524],{"class":456},")\n",[439,526,528,531,534],{"class":441,"line":527},9,[439,529,530],{"class":452},"    def",[439,532,533],{"class":484}," strip",[439,535,536],{"class":456},"(cls, v):\n",[439,538,540,543],{"class":441,"line":539},10,[439,541,542],{"class":452},"        return",[439,544,545],{"class":456}," v.strip()\n",[430,547,549],{"className":432,"code":548,"language":434,"meta":435,"style":435},"# Pydantic v2\nfrom pydantic import BaseModel, field_validator\n\n\nclass V2(BaseModel):\n    name: str\n\n    @field_validator(\"name\")\n    @classmethod                      # Required in v2.\n    def strip(cls, v: str) -> str:\n        return v.strip()\n",[334,550,551,556,567,571,575,588,594,598,609,620,640],{"__ignoreMap":435},[439,552,553],{"class":441,"line":442},[439,554,555],{"class":445},"# Pydantic v2\n",[439,557,558,560,562,564],{"class":441,"line":449},[439,559,453],{"class":452},[439,561,457],{"class":456},[439,563,460],{"class":452},[439,565,566],{"class":456}," BaseModel, field_validator\n",[439,568,569],{"class":441,"line":466},[439,570,470],{"emptyLinePlaceholder":469},[439,572,573],{"class":441,"line":473},[439,574,470],{"emptyLinePlaceholder":469},[439,576,577,579,582,584,586],{"class":441,"line":478},[439,578,481],{"class":452},[439,580,581],{"class":484}," V2",[439,583,488],{"class":456},[439,585,491],{"class":484},[439,587,494],{"class":456},[439,589,590,592],{"class":441,"line":497},[439,591,500],{"class":456},[439,593,504],{"class":503},[439,595,596],{"class":441,"line":507},[439,597,470],{"emptyLinePlaceholder":469},[439,599,600,603,605,607],{"class":441,"line":512},[439,601,602],{"class":484},"    @field_validator",[439,604,488],{"class":456},[439,606,521],{"class":520},[439,608,524],{"class":456},[439,610,611,614,617],{"class":441,"line":527},[439,612,613],{"class":484},"    @",[439,615,616],{"class":503},"classmethod",[439,618,619],{"class":445},"                      # Required in v2.\n",[439,621,622,624,626,629,632,635,637],{"class":441,"line":539},[439,623,530],{"class":452},[439,625,533],{"class":484},[439,627,628],{"class":456},"(cls, v: ",[439,630,631],{"class":503},"str",[439,633,634],{"class":456},") -> ",[439,636,631],{"class":503},[439,638,639],{"class":456},":\n",[439,641,643,645],{"class":441,"line":642},11,[439,644,542],{"class":452},[439,646,545],{"class":456},[425,648,650,651,653,654],{"id":649},"_2-pretrue-modebefore","2. ",[334,652,350],{}," → ",[334,655,354],{},[430,657,659],{"className":432,"code":658,"language":434,"meta":435,"style":435},"@field_validator(\"amount\", mode=\"before\")   # was @validator(\"amount\", pre=True)\n@classmethod\ndef coerce(cls, v: object) -> object:\n    return int(v) if isinstance(v, str) else v\n",[334,660,661,689,697,716],{"__ignoreMap":435},[439,662,663,665,667,670,673,677,680,683,686],{"class":441,"line":442},[439,664,340],{"class":484},[439,666,488],{"class":456},[439,668,669],{"class":520},"\"amount\"",[439,671,672],{"class":456},", ",[439,674,676],{"class":675},"sqxcx","mode",[439,678,679],{"class":452},"=",[439,681,682],{"class":520},"\"before\"",[439,684,685],{"class":456},")   ",[439,687,688],{"class":445},"# was @validator(\"amount\", pre=True)\n",[439,690,691,694],{"class":441,"line":449},[439,692,693],{"class":484},"@",[439,695,696],{"class":503},"classmethod\n",[439,698,699,702,705,707,710,712,714],{"class":441,"line":466},[439,700,701],{"class":452},"def",[439,703,704],{"class":484}," coerce",[439,706,628],{"class":456},[439,708,709],{"class":503},"object",[439,711,634],{"class":456},[439,713,709],{"class":503},[439,715,639],{"class":456},[439,717,718,721,724,727,730,733,736,738,741,744],{"class":441,"line":473},[439,719,720],{"class":452},"    return",[439,722,723],{"class":503}," int",[439,725,726],{"class":456},"(v) ",[439,728,729],{"class":452},"if",[439,731,732],{"class":503}," isinstance",[439,734,735],{"class":456},"(v, ",[439,737,631],{"class":503},[439,739,740],{"class":456},") ",[439,742,743],{"class":452},"else",[439,745,746],{"class":456}," v\n",[425,748,750,751],{"id":749},"_3-cross-field-logic-model_validator","3. Cross-field logic → ",[334,752,369],{},[430,754,756],{"className":432,"code":755,"language":434,"meta":435,"style":435},"from pydantic import model_validator\n\n\nclass DateRange(BaseModel):\n    start: int\n    end: int\n\n    @model_validator(mode=\"after\")    # Replaces a v1 validator reading `values`.\n    def check_order(self) -> \"DateRange\":\n        if self.end \u003C= self.start:\n            raise ValueError(\"end must be after start\")\n        return self\n",[334,757,758,769,773,777,790,798,805,809,829,844,863,878],{"__ignoreMap":435},[439,759,760,762,764,766],{"class":441,"line":442},[439,761,453],{"class":452},[439,763,457],{"class":456},[439,765,460],{"class":452},[439,767,768],{"class":456}," model_validator\n",[439,770,771],{"class":441,"line":449},[439,772,470],{"emptyLinePlaceholder":469},[439,774,775],{"class":441,"line":466},[439,776,470],{"emptyLinePlaceholder":469},[439,778,779,781,784,786,788],{"class":441,"line":473},[439,780,481],{"class":452},[439,782,783],{"class":484}," DateRange",[439,785,488],{"class":456},[439,787,491],{"class":484},[439,789,494],{"class":456},[439,791,792,795],{"class":441,"line":478},[439,793,794],{"class":456},"    start: ",[439,796,797],{"class":503},"int\n",[439,799,800,803],{"class":441,"line":497},[439,801,802],{"class":456},"    end: ",[439,804,797],{"class":503},[439,806,807],{"class":441,"line":507},[439,808,470],{"emptyLinePlaceholder":469},[439,810,811,814,816,818,820,823,826],{"class":441,"line":512},[439,812,813],{"class":484},"    @model_validator",[439,815,488],{"class":456},[439,817,676],{"class":675},[439,819,679],{"class":452},[439,821,822],{"class":520},"\"after\"",[439,824,825],{"class":456},")    ",[439,827,828],{"class":445},"# Replaces a v1 validator reading `values`.\n",[439,830,831,833,836,839,842],{"class":441,"line":527},[439,832,530],{"class":452},[439,834,835],{"class":484}," check_order",[439,837,838],{"class":456},"(self) -> ",[439,840,841],{"class":520},"\"DateRange\"",[439,843,639],{"class":456},[439,845,846,849,852,855,858,860],{"class":441,"line":539},[439,847,848],{"class":452},"        if",[439,850,851],{"class":503}," self",[439,853,854],{"class":456},".end ",[439,856,857],{"class":452},"\u003C=",[439,859,851],{"class":503},[439,861,862],{"class":456},".start:\n",[439,864,865,868,871,873,876],{"class":441,"line":642},[439,866,867],{"class":452},"            raise",[439,869,870],{"class":503}," ValueError",[439,872,488],{"class":456},[439,874,875],{"class":520},"\"end must be after start\"",[439,877,524],{"class":456},[439,879,881,883],{"class":441,"line":880},12,[439,882,542],{"class":452},[439,884,885],{"class":503}," self\n",[393,887,889],{"id":888},"edge-cases-and-gotchas","Edge Cases and Gotchas",[327,891,892,900,912],{},[330,893,894,899],{},[323,895,896,897,359],{},"Missing ",[334,898,344],{}," The most common error; it raises at import.",[330,901,902,907,908,911],{},[323,903,904,359],{},[334,905,906],{},"always=True"," Default validation changed; use ",[334,909,910],{},"validate_default=True"," where you relied on it.",[330,913,914,917,918,921,922,359],{},[323,915,916],{},"Reusable validators."," Consider moving recurring rules to ",[334,919,920],{},"Annotated"," types, per ",[383,923,925],{"href":924},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002F","Creating Reusable Custom Validators",[393,927,929],{"id":928},"verification","Verification",[430,931,933],{"className":432,"code":932,"language":434,"meta":435,"style":435},"import pytest\nfrom pydantic import ValidationError\n\n\ndef test_behaviour_unchanged():\n    assert V2(name=\"  ada  \").name == \"ada\"\n    with pytest.raises(ValidationError):\n        DateRange(start=5, end=1)\n",[334,934,935,942,953,957,961,971,996,1004],{"__ignoreMap":435},[439,936,937,939],{"class":441,"line":442},[439,938,460],{"class":452},[439,940,941],{"class":456}," pytest\n",[439,943,944,946,948,950],{"class":441,"line":449},[439,945,453],{"class":452},[439,947,457],{"class":456},[439,949,460],{"class":452},[439,951,952],{"class":456}," ValidationError\n",[439,954,955],{"class":441,"line":466},[439,956,470],{"emptyLinePlaceholder":469},[439,958,959],{"class":441,"line":473},[439,960,470],{"emptyLinePlaceholder":469},[439,962,963,965,968],{"class":441,"line":478},[439,964,701],{"class":452},[439,966,967],{"class":484}," test_behaviour_unchanged",[439,969,970],{"class":456},"():\n",[439,972,973,976,979,982,984,987,990,993],{"class":441,"line":497},[439,974,975],{"class":452},"    assert",[439,977,978],{"class":456}," V2(",[439,980,981],{"class":675},"name",[439,983,679],{"class":452},[439,985,986],{"class":520},"\"  ada  \"",[439,988,989],{"class":456},").name ",[439,991,992],{"class":452},"==",[439,994,995],{"class":520}," \"ada\"\n",[439,997,998,1001],{"class":441,"line":507},[439,999,1000],{"class":452},"    with",[439,1002,1003],{"class":456}," pytest.raises(ValidationError):\n",[439,1005,1006,1009,1012,1014,1017,1019,1022,1024,1027],{"class":441,"line":512},[439,1007,1008],{"class":456},"        DateRange(",[439,1010,1011],{"class":675},"start",[439,1013,679],{"class":452},[439,1015,1016],{"class":503},"5",[439,1018,672],{"class":456},[439,1020,1021],{"class":675},"end",[439,1023,679],{"class":452},[439,1025,1026],{"class":503},"1",[439,1028,524],{"class":456},[393,1030,1032],{"id":1031},"related-reading","Related Reading",[327,1034,1035,1043],{},[330,1036,1037,1040,1041,359],{},[323,1038,1039],{},"Up to the topic:"," ",[383,1042,386],{"href":385},[330,1044,1045,1040,1048,1052,1053,359],{},[323,1046,1047],{},"Related guides:",[383,1049,1051],{"href":1050},"\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmodel-config-vs-class-config\u002F","model_config vs class Config"," and ",[383,1054,391],{"href":390},[1056,1057,1058],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":435,"searchDepth":449,"depth":449,"links":1060},[1061,1062,1063,1070,1071,1072],{"id":395,"depth":449,"text":396},{"id":405,"depth":449,"text":406},{"id":422,"depth":449,"text":423,"children":1064},[1065,1066,1068],{"id":427,"depth":466,"text":428},{"id":649,"depth":466,"text":1067},"2. pre=True → mode=\"before\"",{"id":749,"depth":466,"text":1069},"3. Cross-field logic → model_validator",{"id":888,"depth":449,"text":889},{"id":928,"depth":449,"text":929},{"id":1031,"depth":449,"text":1032},"Convert Pydantic v1 @validator to v2 @field_validator: the classmethod requirement, before vs after mode replacing pre, accessing other fields, and reusing across fields.","md",{"slug":1076,"type":1077,"breadcrumb":1078,"datePublished":1089,"dateModified":1090,"howto":1091,"faq":1108},"migrate-validator-to-field-validator","long_tail",[1079,1082,1085,1086],{"label":1080,"path":1081},"Home","\u002F",{"label":1083,"path":1084},"Advanced Pydantic Validation & Serialization","\u002Fadvanced-pydantic-validation-serialization\u002F",{"label":386,"path":385},{"label":1087,"path":1088},"Migrate @validator to @field_validator","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Fmigrate-validator-to-field-validator\u002F","2026-03-01","2026-06-18",{"name":73,"steps":1092},[1093,1096,1099,1102,1105],{"name":1094,"text":1095},"Rename the decorator","Replace @validator with @field_validator and add @classmethod beneath it.",{"name":1097,"text":1098},"Translate pre to mode","Replace pre=True with mode='before'; the default is mode='after'.",{"name":1100,"text":1101},"Replace values access","Where you read other fields from the values dict, switch to model_validator or ValidationInfo.",{"name":1103,"text":1104},"Keep multi-field targets","Pass multiple field names to field_validator as before.",{"name":1106,"text":1107},"Run the contract tests","Verify behavior is unchanged against your pinned test suite.",[1109,1112,1115],{"q":1110,"a":1111},"Why does @field_validator require @classmethod?","In Pydantic v2 field validators are class methods by design, so you must stack @classmethod beneath @field_validator. Omitting it raises an error at class definition time. This is a common first stumbling block when migrating from v1, where the decorator implicitly handled the method binding.",{"q":1113,"a":1114},"How do I access other fields in a v2 field validator?","A field_validator sees only its own field. To validate using other fields, move the logic to a model_validator with mode='after', which receives the fully constructed model and can read every field. The v1 pattern of reading the values dict inside a validator maps to model_validator in v2.",{"q":1116,"a":1117},"What replaces pre=True and always=True?","pre=True becomes mode='before', and the default mode='after' runs after coercion. The always behavior changed: v2 runs validators on defaults differently, so review fields that relied on always=True and use validate_default in the field or model config where you need defaults validated.",{"title":73,"description":1073},"B0eAiwDpEbmcXIzIlM109Al2fsLIE7wl5UabZy-GmnY",[1121,1121],null,1781809863211]