Search code examples
pythonfastapipydantic

Using different Pydantic models depending on the value of fields


I have 2 Pydantic models (var1 and var2). The input of the PostExample method can receive data either for the first model or the second. The use of Union helps in solving this issue, but during validation it throws errors for both the first and the second model.

How to make it so that in case of an error in filling in the fields, validator errors are returned only for a certain model, and not for both at once? (if it helps, the models can be distinguished by the length of the field A).

main.py

@app.post("/PostExample")
def postExample(request: Union[schemas.var1, schemas.var2]):
    
    result = post_registration_request.requsest_response()
    return result
  
  

schemas.py

class var1(BaseModel):
    A: str
    B: int
    C: str
    D: str
  
  
class var2(BaseModel):
    A: str
    E: int
    F: str

Solution

  • You could use Discriminated Unions (credits to @larsks for mentioning that in the comments). Setting a discriminated union, "validation is faster since it is only attempted against one model", as well as "only one explicit error is raised in case of failure". Working example is given below.

    Another approach would be to attempt parsing the models (based on a discriminator you pass as query/path param), as described in this answer (Option 1).

    Working Example

    app.py

    import schemas
    from fastapi import FastAPI, Body
    from typing import Union
    
    app = FastAPI()
    
    @app.post("/")
    def submit(item: Union[schemas.Model1, schemas.Model2] = Body(..., discriminator='model_type')):
        return item
    

    schemas.py

    from typing import Literal
    from pydantic import BaseModel
    
    class Model1(BaseModel):
        model_type: Literal['m1']
        A: str
        B: int
        C: str
        D: str
      
    class Model2(BaseModel):
        model_type: Literal['m2']
        A: str
        E: int
        F: str
    

    Test inputs - outputs

    #1 Successful Response   #2 Validation error                   #3 Validation error
                                              
    # Request body           # Request body                        # Request body
    {                        {                                     {
      "model_type": "m1",      "model_type": "m1",                   "model_type": "m2",
      "A": "string",           "A": "string",                        "A": "string",
      "B": 0,                  "C": "string",                        "C": "string",
      "C": "string",           "D": "string"                         "D": "string"
      "D": "string"          }                                     }
    }                                                              
                            
    # Server response        # Server response                     # Server response
    200                      {                                     {
                               "detail": [                           "detail": [
                                 {                                     {
                                   "loc": [                              "loc": [
                                     "body",                               "body",
                                     "Model1",                             "Model2",
                                     "B"                                   "E"
                                   ],                                    ],
                                   "msg": "field required",              "msg": "field required",
                                   "type": "value_error.missing"         "type": "value_error.missing"
                                 }                                     },
                               ]                                       {
                             }                                           "loc": [
                                                                           "body",
                                                                           "Model2",
                                                                           "F"
                                                                         ],
                                                                         "msg": "field required",
                                                                         "type": "value_error.missing"
                                                                       }
                                                                     ]
                                                                   }