[{"data":1,"prerenderedAt":1168},["ShallowReactive",2],{"nav":3,"page-\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002F":152,"surround-\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002F":1166},[4,72],{"title":5,"path":6,"stem":7,"children":8},"Advanced Pydantic Validation Serialization","\u002Fadvanced-pydantic-validation-serialization","advanced-pydantic-validation-serialization",[9,12,24,36,48,54,66],{"title":10,"path":6,"stem":11},"Advanced Pydantic Validation & Serialization","advanced-pydantic-validation-serialization\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Custom Validators & Field Constraints in FastAPI & Pydantic V2","\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints","advanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Findex",[17,18],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21,"children":22},"Creating Reusable Custom Validators in Pydantic: Production Patterns","\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},"JSON Schema Customization","\u002Fadvanced-pydantic-validation-serialization\u002Fjson-schema-customization","advanced-pydantic-validation-serialization\u002Fjson-schema-customization\u002Findex",[29,30],{"title":25,"path":26,"stem":27},{"title":31,"path":32,"stem":33,"children":34},"Customizing OpenAPI Schema Generation in FastAPI: Production Implementation Guide","\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",[35],{"title":31,"path":32,"stem":33},{"title":37,"path":38,"stem":39,"children":40},"Mastering Nested Model Serialization in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fnested-model-serialization","advanced-pydantic-validation-serialization\u002Fnested-model-serialization\u002Findex",[41,42],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45,"children":46},"Handling Deeply Nested JSON Models Efficiently in FastAPI","\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",[47],{"title":43,"path":44,"stem":45},{"title":49,"path":50,"stem":51,"children":52},"Performance Optimization for Models in FastAPI","\u002Fadvanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models","advanced-pydantic-validation-serialization\u002Fperformance-optimization-for-models\u002Findex",[53],{"title":49,"path":50,"stem":51},{"title":55,"path":56,"stem":57,"children":58},"Pydantic V2 Migration Guide: FastAPI Production Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide","advanced-pydantic-validation-serialization\u002Fpydantic-v2-migration-guide\u002Findex",[59,60],{"title":55,"path":56,"stem":57},{"title":61,"path":62,"stem":63,"children":64},"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",[65],{"title":61,"path":62,"stem":63},{"title":67,"path":68,"stem":69,"children":70},"Type Hinting & IDE Integration in FastAPI: Advanced Pydantic Patterns","\u002Fadvanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration","advanced-pydantic-validation-serialization\u002Ftype-hinting-ide-integration\u002Findex",[71],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75,"children":76},"Core Architecture Routing Patterns","\u002Fcore-architecture-routing-patterns","core-architecture-routing-patterns",[77,80,92,104,116,128,140],{"title":78,"path":74,"stem":79},"Core Architecture & Routing Patterns in FastAPI: A Production-Ready Blueprint","core-architecture-routing-patterns\u002Findex",{"title":81,"path":82,"stem":83,"children":84},"Application Factory Patterns in FastAPI: Production Architecture Guide","\u002Fcore-architecture-routing-patterns\u002Fapplication-factory-patterns","core-architecture-routing-patterns\u002Fapplication-factory-patterns\u002Findex",[85,86],{"title":81,"path":82,"stem":83},{"title":87,"path":88,"stem":89,"children":90},"FastAPI App Factory Pattern for Testing and Deployment: Production Guide","\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",[91],{"title":87,"path":88,"stem":89},{"title":93,"path":94,"stem":95,"children":96},"Configuration Management in FastAPI: Production-Ready Patterns & Security","\u002Fcore-architecture-routing-patterns\u002Fconfiguration-management","core-architecture-routing-patterns\u002Fconfiguration-management\u002Findex",[97,98],{"title":93,"path":94,"stem":95},{"title":99,"path":100,"stem":101,"children":102},"Managing Environment Variables with Pydantic Settings in FastAPI","\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",[103],{"title":99,"path":100,"stem":101},{"title":105,"path":106,"stem":107,"children":108},"Dependency Injection Strategies","\u002Fcore-architecture-routing-patterns\u002Fdependency-injection-strategies","core-architecture-routing-patterns\u002Fdependency-injection-strategies\u002Findex",[109,110],{"title":105,"path":106,"stem":107},{"title":111,"path":112,"stem":113,"children":114},"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",[115],{"title":111,"path":112,"stem":113},{"title":117,"path":118,"stem":119,"children":120},"Error Handling & Global Exceptions in FastAPI","\u002Fcore-architecture-routing-patterns\u002Ferror-handling-global-exceptions","core-architecture-routing-patterns\u002Ferror-handling-global-exceptions\u002Findex",[121,122],{"title":117,"path":118,"stem":119},{"title":123,"path":124,"stem":125,"children":126},"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",[127],{"title":123,"path":124,"stem":125},{"title":129,"path":130,"stem":131,"children":132},"Middleware Implementation","\u002Fcore-architecture-routing-patterns\u002Fmiddleware-implementation","core-architecture-routing-patterns\u002Fmiddleware-implementation\u002Findex",[133,134],{"title":129,"path":130,"stem":131},{"title":135,"path":136,"stem":137,"children":138},"Implementing Custom Middleware for Request Tracing in FastAPI","\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",[139],{"title":135,"path":136,"stem":137},{"title":141,"path":142,"stem":143,"children":144},"Modular Router Organization in FastAPI: Production-Grade Architecture","\u002Fcore-architecture-routing-patterns\u002Fmodular-router-organization","core-architecture-routing-patterns\u002Fmodular-router-organization\u002Findex",[145,146],{"title":141,"path":142,"stem":143},{"title":147,"path":148,"stem":149,"children":150},"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",[151],{"title":147,"path":148,"stem":149},{"id":153,"title":19,"body":154,"description":1161,"extension":1162,"meta":1163,"navigation":343,"path":20,"seo":1164,"stem":21,"__hash__":1165},"content\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002Fcreating-reusable-custom-validators-in-pydantic\u002Findex.md",{"type":155,"value":156,"toc":1153},"minimark",[157,161,170,175,183,258,290,294,297,650,655,686,690,704,930,935,957,961,964,1026,1030,1098,1102,1107,1115,1120,1137,1142,1149],[158,159,19],"h1",{"id":160},"creating-reusable-custom-validators-in-pydantic-production-patterns",[162,163,164,165,169],"p",{},"Architecting DRY, reusable validation logic in Pydantic v2 requires a shift from legacy decorator patterns to explicit execution hooks and factory functions. This guide details production-ready strategies for FastAPI applications, focusing on parameterized checks, execution boundaries, and strict error handling. For broader context on the validation lifecycle and serialization hooks, refer to the ",[166,167,10],"a",{"href":168},"\u002Fadvanced-pydantic-validation-serialization\u002F"," documentation.",[171,172,174],"h2",{"id":173},"understanding-pydantic-v2-validator-architecture","Understanding Pydantic V2 Validator Architecture",[162,176,177,178,182],{},"Pydantic v2 replaced the monolithic ",[179,180,181],"code",{},"@validator"," with granular execution modes. Understanding these semantics prevents silent data corruption and validation bottlenecks.",[184,185,186,202],"table",{},[187,188,189],"thead",{},[190,191,192,196,199],"tr",{},[193,194,195],"th",{},"Mode",[193,197,198],{},"Execution Point",[193,200,201],{},"Use Case",[203,204,205,219,232,245],"tbody",{},[190,206,207,213,216],{},[208,209,210],"td",{},[179,211,212],{},"'before'",[208,214,215],{},"Runs on raw input before type coercion",[208,217,218],{},"Sanitizing strings, parsing custom formats, normalizing payloads",[190,220,221,226,229],{},[208,222,223],{},[179,224,225],{},"'after'",[208,227,228],{},"Runs after successful type coercion",[208,230,231],{},"Range checks, cross-reference validation, business rule enforcement",[190,233,234,239,242],{},[208,235,236],{},[179,237,238],{},"'wrap'",[208,240,241],{},"Intercepts the entire validation pipeline",[208,243,244],{},"Logging, caching, or modifying validation call chains (high overhead)",[190,246,247,252,255],{},[208,248,249],{},[179,250,251],{},"'plain'",[208,253,254],{},"Bypasses Pydantic's internal validation entirely",[208,256,257],{},"Custom parsing logic where you handle all type conversion manually",[162,259,260,264,265,268,269,272,273,276,277,280,281,284,285,289],{},[261,262,263],"strong",{},"Execution Order:"," ",[179,266,267],{},"before"," → type coercion → ",[179,270,271],{},"after"," → ",[179,274,275],{},"model_validator"," (if applicable). When designing constraints, reserve ",[179,278,279],{},"@field_validator"," for single-field logic and ",[179,282,283],{},"@model_validator"," for relational rules. Detailed constraint implementation strategies are covered in ",[166,286,288],{"href":287},"\u002Fadvanced-pydantic-validation-serialization\u002Fcustom-validators-field-constraints\u002F","Custom Validators & Field Constraints",".",[171,291,293],{"id":292},"building-reusable-field-validators","Building Reusable Field Validators",[162,295,296],{},"Hardcoding validation rules inside model classes violates DRY principles and complicates unit testing. Instead, use factory functions with closures to bind configuration parameters at runtime.",[298,299,304],"pre",{"className":300,"code":301,"language":302,"meta":303,"style":303},"language-python shiki shiki-themes github-light","from pydantic import BaseModel, field_validator\nfrom typing import Any, Callable\n\ndef range_validator(min_val: float, max_val: float) -> Callable[[Any], float]:\n \"\"\"Returns a configured validator function for numeric range enforcement.\"\"\"\n def _validate(v: Any) -> float:\n if not isinstance(v, (int, float)):\n raise ValueError(\"Input must be numeric\")\n if not (min_val \u003C= v \u003C= max_val):\n raise ValueError(f\"Value must be between {min_val} and {max_val}\")\n return float(v)\n return _validate\n\nclass SensorReading(BaseModel):\n temperature: float\n humidity: float\n\n # Apply factory-generated validators to specific fields\n _validate_temp = field_validator(\"temperature\", mode=\"after\")(range_validator(-50.0, 120.0))\n _validate_humidity = field_validator(\"humidity\", mode=\"after\")(range_validator(0.0, 100.0))\n","python","",[179,305,306,325,338,345,375,382,399,425,443,464,503,515,523,528,545,554,562,567,574,617],{"__ignoreMap":303},[307,308,311,315,319,322],"span",{"class":309,"line":310},"line",1,[307,312,314],{"class":313},"sD7c4","from",[307,316,318],{"class":317},"sgsFI"," pydantic ",[307,320,321],{"class":313},"import",[307,323,324],{"class":317}," BaseModel, field_validator\n",[307,326,328,330,333,335],{"class":309,"line":327},2,[307,329,314],{"class":313},[307,331,332],{"class":317}," typing ",[307,334,321],{"class":313},[307,336,337],{"class":317}," Any, Callable\n",[307,339,341],{"class":309,"line":340},3,[307,342,344],{"emptyLinePlaceholder":343},true,"\n",[307,346,348,351,355,358,362,365,367,370,372],{"class":309,"line":347},4,[307,349,350],{"class":313},"def",[307,352,354],{"class":353},"s7eDp"," range_validator",[307,356,357],{"class":317},"(min_val: ",[307,359,361],{"class":360},"sYu0t","float",[307,363,364],{"class":317},", max_val: ",[307,366,361],{"class":360},[307,368,369],{"class":317},") -> Callable[[Any], ",[307,371,361],{"class":360},[307,373,374],{"class":317},"]:\n",[307,376,378],{"class":309,"line":377},5,[307,379,381],{"class":380},"sYBdl"," \"\"\"Returns a configured validator function for numeric range enforcement.\"\"\"\n",[307,383,385,388,391,394,396],{"class":309,"line":384},6,[307,386,387],{"class":313}," def",[307,389,390],{"class":353}," _validate",[307,392,393],{"class":317},"(v: Any) -> ",[307,395,361],{"class":360},[307,397,398],{"class":317},":\n",[307,400,402,405,408,411,414,417,420,422],{"class":309,"line":401},7,[307,403,404],{"class":313}," if",[307,406,407],{"class":313}," not",[307,409,410],{"class":360}," isinstance",[307,412,413],{"class":317},"(v, (",[307,415,416],{"class":360},"int",[307,418,419],{"class":317},", ",[307,421,361],{"class":360},[307,423,424],{"class":317},")):\n",[307,426,428,431,434,437,440],{"class":309,"line":427},8,[307,429,430],{"class":313}," raise",[307,432,433],{"class":360}," ValueError",[307,435,436],{"class":317},"(",[307,438,439],{"class":380},"\"Input must be numeric\"",[307,441,442],{"class":317},")\n",[307,444,446,448,450,453,456,459,461],{"class":309,"line":445},9,[307,447,404],{"class":313},[307,449,407],{"class":313},[307,451,452],{"class":317}," (min_val ",[307,454,455],{"class":313},"\u003C=",[307,457,458],{"class":317}," v ",[307,460,455],{"class":313},[307,462,463],{"class":317}," max_val):\n",[307,465,467,469,471,473,476,479,482,485,488,491,493,496,498,501],{"class":309,"line":466},10,[307,468,430],{"class":313},[307,470,433],{"class":360},[307,472,436],{"class":317},[307,474,475],{"class":313},"f",[307,477,478],{"class":380},"\"Value must be between ",[307,480,481],{"class":360},"{",[307,483,484],{"class":317},"min_val",[307,486,487],{"class":360},"}",[307,489,490],{"class":380}," and ",[307,492,481],{"class":360},[307,494,495],{"class":317},"max_val",[307,497,487],{"class":360},[307,499,500],{"class":380},"\"",[307,502,442],{"class":317},[307,504,506,509,512],{"class":309,"line":505},11,[307,507,508],{"class":313}," return",[307,510,511],{"class":360}," float",[307,513,514],{"class":317},"(v)\n",[307,516,518,520],{"class":309,"line":517},12,[307,519,508],{"class":313},[307,521,522],{"class":317}," _validate\n",[307,524,526],{"class":309,"line":525},13,[307,527,344],{"emptyLinePlaceholder":343},[307,529,531,534,537,539,542],{"class":309,"line":530},14,[307,532,533],{"class":313},"class",[307,535,536],{"class":353}," SensorReading",[307,538,436],{"class":317},[307,540,541],{"class":353},"BaseModel",[307,543,544],{"class":317},"):\n",[307,546,548,551],{"class":309,"line":547},15,[307,549,550],{"class":317}," temperature: ",[307,552,553],{"class":360},"float\n",[307,555,557,560],{"class":309,"line":556},16,[307,558,559],{"class":317}," humidity: ",[307,561,553],{"class":360},[307,563,565],{"class":309,"line":564},17,[307,566,344],{"emptyLinePlaceholder":343},[307,568,570],{"class":309,"line":569},18,[307,571,573],{"class":572},"sAwPA"," # Apply factory-generated validators to specific fields\n",[307,575,577,580,583,586,589,591,595,597,600,603,606,609,611,614],{"class":309,"line":576},19,[307,578,579],{"class":317}," _validate_temp ",[307,581,582],{"class":313},"=",[307,584,585],{"class":317}," field_validator(",[307,587,588],{"class":380},"\"temperature\"",[307,590,419],{"class":317},[307,592,594],{"class":593},"sqxcx","mode",[307,596,582],{"class":313},[307,598,599],{"class":380},"\"after\"",[307,601,602],{"class":317},")(range_validator(",[307,604,605],{"class":313},"-",[307,607,608],{"class":360},"50.0",[307,610,419],{"class":317},[307,612,613],{"class":360},"120.0",[307,615,616],{"class":317},"))\n",[307,618,620,623,625,627,630,632,634,636,638,640,643,645,648],{"class":309,"line":619},20,[307,621,622],{"class":317}," _validate_humidity ",[307,624,582],{"class":313},[307,626,585],{"class":317},[307,628,629],{"class":380},"\"humidity\"",[307,631,419],{"class":317},[307,633,594],{"class":593},[307,635,582],{"class":313},[307,637,599],{"class":380},[307,639,602],{"class":317},[307,641,642],{"class":360},"0.0",[307,644,419],{"class":317},[307,646,647],{"class":360},"100.0",[307,649,616],{"class":317},[162,651,652],{},[261,653,654],{},"Production Notes:",[656,657,658,669,676],"ul",{},[659,660,661,662,664,665,668],"li",{},"Always return the validated\u002Fcoerced value from ",[179,663,271],{}," validators. Returning ",[179,666,667],{},"None"," or omitting the return statement breaks the validation pipeline.",[659,670,671,672,675],{},"Use ",[179,673,674],{},"typing.Any"," for input parameters to safely handle pre-coercion raw data, then cast explicitly.",[659,677,678,679,490,682,685],{},"For generic constraints, leverage ",[179,680,681],{},"typing.TypeVar",[179,683,684],{},"Protocol"," to enforce type safety across heterogeneous schemas.",[171,687,689],{"id":688},"cross-field-and-model-level-validation-patterns","Cross-Field and Model-Level Validation Patterns",[162,691,692,693,696,697,699,700,703],{},"Relational business rules (e.g., ",[179,694,695],{},"start_date \u003C end_date",", tier-based limits) require ",[179,698,283],{},". Use ",[179,701,702],{},"mode=\"after\""," to guarantee all fields have passed individual type checks before evaluating dependencies.",[298,705,707],{"className":300,"code":706,"language":302,"meta":303,"style":303},"from pydantic import BaseModel, model_validator, ValidationInfo\nfrom typing import Self\n\nclass UserTier(BaseModel):\n tier: str\n monthly_limit: int\n\n @model_validator(mode=\"after\")\n def check_tier_limits(self) -> Self:\n limits = {\"basic\": 100, \"pro\": 1000, \"enterprise\": 10000}\n max_allowed = limits.get(self.tier, 0)\n \n if self.monthly_limit > max_allowed:\n raise ValueError(f\"Limit {self.monthly_limit} exceeds {self.tier} tier allowance ({max_allowed})\")\n return self\n",[179,708,709,720,731,735,748,756,764,768,783,793,835,856,861,877,923],{"__ignoreMap":303},[307,710,711,713,715,717],{"class":309,"line":310},[307,712,314],{"class":313},[307,714,318],{"class":317},[307,716,321],{"class":313},[307,718,719],{"class":317}," BaseModel, model_validator, ValidationInfo\n",[307,721,722,724,726,728],{"class":309,"line":327},[307,723,314],{"class":313},[307,725,332],{"class":317},[307,727,321],{"class":313},[307,729,730],{"class":317}," Self\n",[307,732,733],{"class":309,"line":340},[307,734,344],{"emptyLinePlaceholder":343},[307,736,737,739,742,744,746],{"class":309,"line":347},[307,738,533],{"class":313},[307,740,741],{"class":353}," UserTier",[307,743,436],{"class":317},[307,745,541],{"class":353},[307,747,544],{"class":317},[307,749,750,753],{"class":309,"line":377},[307,751,752],{"class":317}," tier: ",[307,754,755],{"class":360},"str\n",[307,757,758,761],{"class":309,"line":384},[307,759,760],{"class":317}," monthly_limit: ",[307,762,763],{"class":360},"int\n",[307,765,766],{"class":309,"line":401},[307,767,344],{"emptyLinePlaceholder":343},[307,769,770,773,775,777,779,781],{"class":309,"line":427},[307,771,772],{"class":353}," @model_validator",[307,774,436],{"class":317},[307,776,594],{"class":593},[307,778,582],{"class":313},[307,780,599],{"class":380},[307,782,442],{"class":317},[307,784,785,787,790],{"class":309,"line":445},[307,786,387],{"class":313},[307,788,789],{"class":353}," check_tier_limits",[307,791,792],{"class":317},"(self) -> Self:\n",[307,794,795,798,800,803,806,809,812,814,817,819,822,824,827,829,832],{"class":309,"line":466},[307,796,797],{"class":317}," limits ",[307,799,582],{"class":313},[307,801,802],{"class":317}," {",[307,804,805],{"class":380},"\"basic\"",[307,807,808],{"class":317},": ",[307,810,811],{"class":360},"100",[307,813,419],{"class":317},[307,815,816],{"class":380},"\"pro\"",[307,818,808],{"class":317},[307,820,821],{"class":360},"1000",[307,823,419],{"class":317},[307,825,826],{"class":380},"\"enterprise\"",[307,828,808],{"class":317},[307,830,831],{"class":360},"10000",[307,833,834],{"class":317},"}\n",[307,836,837,840,842,845,848,851,854],{"class":309,"line":505},[307,838,839],{"class":317}," max_allowed ",[307,841,582],{"class":313},[307,843,844],{"class":317}," limits.get(",[307,846,847],{"class":360},"self",[307,849,850],{"class":317},".tier, ",[307,852,853],{"class":360},"0",[307,855,442],{"class":317},[307,857,858],{"class":309,"line":517},[307,859,860],{"class":317}," \n",[307,862,863,865,868,871,874],{"class":309,"line":525},[307,864,404],{"class":313},[307,866,867],{"class":360}," self",[307,869,870],{"class":317},".monthly_limit ",[307,872,873],{"class":313},">",[307,875,876],{"class":317}," max_allowed:\n",[307,878,879,881,883,885,887,890,893,896,898,901,903,906,908,911,913,916,918,921],{"class":309,"line":530},[307,880,430],{"class":313},[307,882,433],{"class":360},[307,884,436],{"class":317},[307,886,475],{"class":313},[307,888,889],{"class":380},"\"Limit ",[307,891,892],{"class":360},"{self",[307,894,895],{"class":317},".monthly_limit",[307,897,487],{"class":360},[307,899,900],{"class":380}," exceeds ",[307,902,892],{"class":360},[307,904,905],{"class":317},".tier",[307,907,487],{"class":360},[307,909,910],{"class":380}," tier allowance (",[307,912,481],{"class":360},[307,914,915],{"class":317},"max_allowed",[307,917,487],{"class":360},[307,919,920],{"class":380},")\"",[307,922,442],{"class":317},[307,924,925,927],{"class":309,"line":547},[307,926,508],{"class":313},[307,928,929],{"class":360}," self\n",[162,931,932],{},[261,933,934],{},"Request-Scoped Context & Performance:",[656,936,937,948,951],{},[659,938,939,940,943,944,947],{},"Access ",[179,941,942],{},"info: ValidationInfo"," in validators to inject request-scoped data (e.g., ",[179,945,946],{},"info.context.get(\"user_role\")","). This avoids passing external state through model constructors.",[659,949,950],{},"Avoid N+1 validation overhead in nested structures by validating at the parent model level rather than attaching identical validators to every child instance.",[659,952,953,954,956],{},"Keep ",[179,955,275],{}," logic stateless and O(1) where possible. Database lookups or external API calls should be deferred to FastAPI dependencies.",[171,958,960],{"id":959},"integrating-validators-with-fastapi","Integrating Validators with FastAPI",[162,962,963],{},"Pydantic validation integrates seamlessly with FastAPI's routing and dependency injection system.",[965,966,967,981,995],"ol",{},[659,968,969,972,973,976,977,980],{},[261,970,971],{},"Custom Exception Handlers:"," Pydantic raises ",[179,974,975],{},"pydantic.ValidationError"," on failure. FastAPI automatically converts this to a ",[179,978,979],{},"422 Unprocessable Entity"," response. Override the default behavior by registering a custom exception handler to standardize error payloads across microservices.",[659,982,983,990,991,994],{},[261,984,985,986,989],{},"Pre-Validation via ",[179,987,988],{},"Depends",":"," Use ",[179,992,993],{},"Depends()"," to run validation logic before model instantiation when dealing with complex authentication, multi-step workflows, or external service lookups.",[659,996,997,1000,1001,1004,1005,1008,1009,1011,1012,1015,1016,419,1019,419,1022,1025],{},[261,998,999],{},"OpenAPI Compliance:"," Validators using ",[179,1002,1003],{},"mode=\"before\""," or ",[179,1006,1007],{},"mode=\"plain\""," do not generate JSON Schema constraints. Always prefer ",[179,1010,702],{}," or Pydantic's built-in ",[179,1013,1014],{},"Field()"," constraints (",[179,1017,1018],{},"gt",[179,1020,1021],{},"regex",[179,1023,1024],{},"max_length",") to ensure accurate OpenAPI documentation and client SDK generation.",[171,1027,1029],{"id":1028},"common-production-pitfalls","Common Production Pitfalls",[184,1031,1032,1045],{},[187,1033,1034],{},[190,1035,1036,1039,1042],{},[193,1037,1038],{},"Anti-Pattern",[193,1040,1041],{},"Impact",[193,1043,1044],{},"Correct Approach",[203,1046,1047,1066,1077],{},[190,1048,1049,1056,1059],{},[208,1050,1051,1052,1055],{},"Using ",[179,1053,1054],{},"mode=\"wrap\""," for simple coercion",[208,1057,1058],{},"Adds ~30-50% validation overhead per field",[208,1060,671,1061,1004,1063,1065],{},[179,1062,1003],{},[179,1064,702],{}," unless intercepting the validation chain is strictly necessary",[190,1067,1068,1071,1074],{},[208,1069,1070],{},"Hardcoding rules inside model classes",[208,1072,1073],{},"Breaks DRY, complicates mocking\u002Ftesting",[208,1075,1076],{},"Extract logic into parameterized factory functions or standalone validator modules",[190,1078,1079,1085,1088],{},[208,1080,1081,1082],{},"Raising generic ",[179,1083,1084],{},"Exception",[208,1086,1087],{},"Bypasses Pydantic's error aggregation and FastAPI's 422 auto-formatting",[208,1089,1090,1091,1094,1095,1097],{},"Always raise ",[179,1092,1093],{},"ValueError"," for field-level failures or ",[179,1096,975],{}," for model-level aggregation",[171,1099,1101],{"id":1100},"frequently-asked-questions","Frequently Asked Questions",[162,1103,1104],{},[261,1105,1106],{},"Can I reuse validators across multiple Pydantic models?",[162,1108,1109,1110,1004,1112,1114],{},"Yes. Define validator factories or standalone functions, then apply them via ",[179,1111,279],{},[179,1113,283],{},". Parameterize them using closures or configuration dataclasses to adapt to different schema requirements without duplication.",[162,1116,1117],{},[261,1118,1119],{},"How do I pass dynamic parameters to a custom validator?",[162,1121,1122,1123,1126,1127,1130,1131,1133,1134,1136],{},"Leverage Python closures or ",[179,1124,1125],{},"functools.partial"," to bind configuration at runtime. For request-scoped parameters (e.g., tenant IDs, user roles), inject them via ",[179,1128,1129],{},"info.context"," in ",[179,1132,283],{}," or use FastAPI's ",[179,1135,988],{}," to attach context before model instantiation.",[162,1138,1139],{},[261,1140,1141],{},"Does Pydantic v2 support async validators?",[162,1143,1144,1145,1148],{},"No. Pydantic validators are strictly synchronous to maintain C-core performance guarantees and compatibility with the validation loop. Handle async operations (DB queries, external API calls) in FastAPI dependencies prior to model instantiation, or use ",[179,1146,1147],{},"asyncio.to_thread"," for blocking I\u002FO if absolutely necessary.",[1150,1151,1152],"style",{},"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 pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}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);}",{"title":303,"searchDepth":327,"depth":327,"links":1154},[1155,1156,1157,1158,1159,1160],{"id":173,"depth":327,"text":174},{"id":292,"depth":327,"text":293},{"id":688,"depth":327,"text":689},{"id":959,"depth":327,"text":960},{"id":1028,"depth":327,"text":1029},{"id":1100,"depth":327,"text":1101},"Architecting DRY, reusable validation logic in Pydantic v2 requires a shift from legacy decorator patterns to explicit execution hooks and factory…","md",{},{"title":19,"description":1161},"x2m_Opv6s_AHURPZ96hk9E_sRc-fFN-HobI8bdCcDc8",[1167,1167],null,1778082655284]