datamodel-code-generator icon indicating copy to clipboard operation
datamodel-code-generator copied to clipboard

Forward-compatible usage of Pydantic v1

Open aaronsteers opened this issue 1 year ago • 0 comments

Is your feature request related to a problem? Please describe.

I love that this codegen tool exists! We currently rely on models generated with this library, but we need to support compatibility with both Pydantic v1 and v2. I'm struggling with how to do this.

Describe the solution you'd like

I'd like datamodel-code-generator to be able to generate "v1" Pydantic models, but in a way that also forwards-compatible with Pydantic 2.

This can be implemented with the following:

# We use Pydantic 1.x for backwards compatibility. We import it in a
# way that is also forwards compatible with Pydantic 2.x.
try:
    import pydantic.v1 as pydantic_v1
    # If this succeeds we are using Pydantic 2.x, and we import the backwards-compatible
    # version of BaseModel.
except ImportError:
    # In this case, we are using Pydantic 1.x and we import as usual.
    import pydantic as pydantic_v1

...

from pydantic_v1 import BaseModel, ...

...

Describe alternatives you've considered

I tried using a modified version of the above and then specifying an override base class of ..BaseModel_v1:

# We use Pydantic 1.x for backwards compatibility. We import it in a
# way that is also forwards compatible with Pydantic 2.x.
try:
    import pydantic.v1 import BaseModel as BaseModel_v1
    # If this succeeds we are using Pydantic 2.x, but we import the backwards-compatible
    # version of BaseModel.
except ImportError:
    # In this case, we are using Pydantic 1.x and we import as usual.
    import pydantic import BaseModel as BaseModel_v1

Any way I try implement the base class override, however, I still end up with an extra (and incorrect) import statement of BaseModel_v1. I tried the base class of BaseModel_v1, .BaseModel_v1, and ..BaseModel_v1.

Ideally, for this workaround to work, an override base class without a . in it would be expected to have already been imported, since its impossible to import a class without a module reference and so rather than import BaseModel_v1 being generated if I set base class of BaseModel_v1, that scenario would just skip an attempt to import.

The closest I could get was this invocation, using the above code in the header file:

  datamodel-codegen \
    --input "$ROOT_DIR/$YAML_DIR/$filename_wo_ext.yaml" \
    --output "$ROOT_DIR/$OUTPUT_DIR/$filename_wo_ext.py" \
    --use-title-as-name \
    --disable-timestamp \
    --input-file-type jsonschema \
    --output-model-type pydantic.BaseModel \
    --base-class "..BaseModel_v1" \
    --custom-file-header-path "protocol-models/python/generation-file-header.py" \
    --target-python-version 3.7

Additional context

WIP PR in downstream library here:

  • https://github.com/airbytehq/airbyte-protocol/pull/77

aaronsteers avatar May 07 '24 05:05 aaronsteers