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.77k stars 304 forks source link

Serialization of subclasses changed in pydantic v2 #1807

Open igorbanfi opened 10 months ago

igorbanfi commented 10 months ago

Describe the bug

Due to changes in how serialization of subclasses is handled in pydantic v2, generated models do not produce correct output when using model_dump or model_dump_json.

See issue https://github.com/pydantic/pydantic/issues/7326

To Reproduce

Example schema:


 paths:
  /pets:
    patch:
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
              - $ref: '#/components/schemas/Cat'
              - $ref: '#/components/schemas/Dog'
              discriminator:
                propertyName: pet_type
      responses:
        '200':
          description: Updated
components:
  schemas:
    Animals:
      type: object
      properties:
        pet:
          $ref: '#/components/schemas/Pet'
    Pet:
      type: object
      required:
      - pet_type
      properties:
        pet_type:
          type: string
      discriminator:
        propertyName: pet_type
    Dog: # "Dog" is a value for the pet_type property (the discriminator value)
      allOf: # Combines the main `Pet` schema with `Dog`-specific properties 
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Dog`
        properties:
          bark:
            type: boolean
          breed:
            type: string
            enum: [Dingo, Husky, Retriever, Shepherd]
    Cat: # "Cat" is a value for the pet_type property (the discriminator value)
      allOf: # Combines the main `Pet` schema with `Cat`-specific properties 
      - $ref: '#/components/schemas/Pet'
      - type: object
        # all other properties specific to a `Cat`
        properties:
          hunts:
            type: boolean
          age:
            type: integer

Used commandline:

$ datamodel-codegen --input pets.yaml --output pets.py --input-file-type openapi --output-model-type pydantic_v2.BaseModel

Generated models:


# generated by datamodel-codegen:
#   filename:  pets.yaml
#   timestamp: 2024-01-12T22:40:02+00:00

from __future__ import annotations

from enum import Enum
from typing import Optional

from pydantic import BaseModel

class Pet(BaseModel):
    pet_type: str

class Breed(Enum):
    Dingo = 'Dingo'
    Husky = 'Husky'
    Retriever = 'Retriever'
    Shepherd = 'Shepherd'

class Dog(Pet):
    bark: Optional[bool] = None
    breed: Optional[Breed] = None

class Cat(Pet):
    hunts: Optional[bool] = None
    age: Optional[int] = None

class Animals(BaseModel):
    pet: Optional[Pet] = None

and their usage:


import pets

animals = pets.Animals(pet=pets.Cat(pet_type='cat', hunts=True, age=2))

print(animals)
# Animals(pet=Cat(pet_type='cat', hunts=True, age=2))

print(animals.model_dump_json())
# '{"pet":{"pet_type":"cat"}}'

Expected behavior To be able to serialize with all data from subclasses. One way to achieve this is to specify type of pet field in Animals with SerializeAsAny (docs). Another way is to specify all possible subclasses that pet field can take with union.

Version:

osolmaz commented 2 months ago

Impacted by this as well