koxudaxi / datamodel-code-generator

Pydantic model and dataclasses.dataclass generator for easy conversion of JSON, OpenAPI, JSON Schema, and YAML data sources.
https://koxudaxi.github.io/datamodel-code-generator/
MIT License
2.78k stars 305 forks source link

Add a --default-factory flag to set nested types #1426

Open jhammarstedt opened 1 year ago

jhammarstedt commented 1 year ago

Is your feature request related to a problem? Please describe. I'm using pydantic to parse raw data in a dataflow pipeline. This comes with a lot of nested data models where the inner field sometimes will be empty. These will cause error when calling a nested model that does not exist. The current solution is just to use: x = A.B.c if A.B else None similar to the x = A.get("B",{}).get("c") when using dictionaries.

Describe the solution you'd like I want a flag --default-factory that sets the default value to an empty version of the datamodel:

Hence: Currently a normal generation gives:

class B_model(BaseModel):
 c = Optional[List]=None

class A_model(BaseModel):
 B = Optional[B_model] = None

This works with input json{"A":{"B": {"c":[]} }} but I could also get {"A":None}.

class B_model(BaseModel):
 c = Optional[List]= None

class A_model(BaseModel):
 B = Optional[B_model] = Field(default_factory=B_model)

By then complementing this solution with a model_validator:

  @model_validator(mode="before")
  def default_children(cls, values):
      for key, value in values.items():
          if value is None and (default_factory := getattr(cls.model_fields[key], "default_factory")):
              values[key] = default_factory()
      return values

I'm able to safely call anything which will just return a None if the field does not exist.

Describe alternatives you've considered The current solution is just to use: x = A.B.c if A.B else None similar to the x = A.get("B",{}).get("c") when using dictionaries. One could also overwrite the pydantic class methods to do something similar or use Field validators.

keean commented 1 year ago

I need default factories for generating "@dataclass" classes like this:

Current output:

@dataclass
class CustomOriginConfig:
    OriginProtocolPolicy: str
    HTTPPort: Optional[int] = 80
    HTTPSPort: Optional[int] = 443
    OriginKeepaliveTimeout: Optional[int] = 5
    OriginReadTimeout: Optional[int] = 30
    OriginSSLProtocols: Optional[List[str]] = ['TLSv1', 'SSLv3']

but this doesn't pass typechecking (running uvicorn)

What is required:

@dataclass
class CustomOriginConfig:
    OriginProtocolPolicy: str
    HTTPPort: Optional[int] = 80
    HTTPSPort: Optional[int] = 443
    OriginKeepaliveTimeout: Optional[int] = 5
    OriginReadTimeout: Optional[int] = 30
    OriginSSLProtocols: Optional[List[str]] = field(default_factory=lambda: ['TLSv1', 'SSLv3'])