datamodel-code-generator
datamodel-code-generator copied to clipboard
Discriminated union with a List: annotation is on the list, not the union
Describe the bug
Generation of list/array of discriminated union annotates the list, not the union. Same problem with = Field(..., discriminator) too if you don't use annotated.
To Reproduce
Example schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "SomeTest",
"type": "object",
"required": [
"some_array"
],
"properties": {
"some_array": {
"type": "array",
"items": {
"discriminator": {
"propertyName": "discrim",
"mapping": {
"Type1": "#/definitions/FeatureType1",
"Type2": "#/definitions/FeatureType2"
}
},
"oneOf": [
{
"$ref": "#/definitions/FeatureType1"
},
{
"$ref": "#/definitions/FeatureType2"
}
]
}
}
},
"definitions": {
"FeatureType1": {
"type": "object",
"properties": {
"discrim": {
"type": "string"
},
"id": {
"type": "string"
}
}
},
"FeatureType2": {
"type": "object",
"properties": {
"discrim": {
"type": "string"
},
"id": {
"type": "string"
},
"something_else": {
"type": "string"
}
}
}
}
}
Used commandline:
$ datamodel-codegen --disable-timestamp --use-annotated --collapse-root-models --target-python-version '3.11' --input ../schema.json --input-file-type jsonschema --output whatever.py
This gives:
# generated by datamodel-codegen:
# filename: schema.json
from __future__ import annotations
from typing import Annotated, List, Literal, Optional, Union
from pydantic import BaseModel, Field
class FeatureType1(BaseModel):
discrim: Literal['Type1']
id: Optional[str] = None
class FeatureType2(BaseModel):
discrim: Literal['Type2']
id: Optional[str] = None
something_else: Optional[str] = None
class SomeTest(BaseModel):
some_array: Annotated[
List[Union[FeatureType1, FeatureType2]], Field(discriminator='discrim')
]
This doesn't work:
TypeError: `discriminator` can only be used with `Union` type with more than one variant`
Expected behavior
The problem is that the Annotation should be on the Union, not on the list, so the SomeTest class should be:
class SomeTest(BaseModel):
some_array: List[
Annotated[Union[FeatureType1, FeatureType2], Field(discriminator='discrim')]
]
And now it works. Note that we get more or less the same problem if you remove --use-annotated
Version:
- OS: Windows 11
- Python version: 3.11
- datamodel-code-generator version: 0.25.4
Additional context
Thanks!
That behavior was ignored by Pydantic until 2.8.2, but starting 2.9.0 it end up with the error on generated model:
TypeError: 'list' is not a valid discriminated union variant; should be a BaseModelordataclass``
Maybe there is workaround to bypass it?
This is blocking us from upgrading pydantic, does anyone have any workaround?
This is blocking us from upgrading pydantic, does anyone have any workaround?
the same issue :/
this is huge
We just wrote scripts that processed the generated file into a working one and added them to the build/make files. Not ideal, but gets the job done
Same issue here
PR welcome.
I have found that this problem only occurs if you use --collapse-root-models option. Without this option, the generated models seem to import fine with newer pydantic2.
I have found that this problem only occurs if you use --collapse-root-models option. Without this option, the generated models seem to import fine with newer pydantic2.
That's what I was missing when investigating thank you! I've opened a PR with a small change that fixes my case but I'll need maintainer input on whether or not it's a good fix for the general case.