New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSON schema generation of Discriminated Unions doesn't work with lists #3608
Comments
Maybe the title should be more specific. The parsing works correctly AFAICS. The JSON schema generation doesn't. |
Hi all, this workaround seems to work for me, until using the Annotation directly is fixed for lists: from typing import Literal, Annotated, Union
from pydantic import BaseModel, Field
class Dog(BaseModel):
animal_type: Literal['dog']
name: str
claw: str
class Bird(BaseModel):
animal_type: Literal['bird']
name: str
wing_size: str
class Animal(BaseModel):
__root__: Annotated[
Union[
Dog,
Bird
],
Field(discriminator='animal_type')
]
# Animal = Annotated[
# Union[
# Dog,
# Bird
# ],
# Field(discriminator='animal_type')
# ]
class Zoo(BaseModel):
animals: list[Animal]
print(Zoo.schema_json()) The schema I get: {
"title": "Zoo",
"type": "object",
"properties": {
"animals": {
"title": "Animals",
"type": "array",
"items": {
"$ref": "#/definitions/Animal"
}
}
},
"required": [
"animals"
],
"definitions": {
"Dog": {
"title": "Dog",
"type": "object",
"properties": {
"animal_type": {
"title": "Animal Type",
"enum": [
"dog"
],
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"claw": {
"title": "Claw",
"type": "string"
}
},
"required": [
"animal_type",
"name",
"claw"
]
},
"Bird": {
"title": "Bird",
"type": "object",
"properties": {
"animal_type": {
"title": "Animal Type",
"enum": [
"bird"
],
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"wing_size": {
"title": "Wing Size",
"type": "string"
}
},
"required": [
"animal_type",
"name",
"wing_size"
]
},
"Animal": {
"title": "Animal",
"discriminator": {
"propertyName": "animal_type",
"mapping": {
"dog": "#/definitions/Dog",
"bird": "#/definitions/Bird"
}
},
"anyOf": [
{
"$ref": "#/definitions/Dog"
},
{
"$ref": "#/definitions/Bird"
}
]
}
}
} |
@nayef-livio-derwiche thank, it worked for me ! I just had to add this to the Animal class, to be able to use dot notation for getting attributes: def __getattr__(self, item): # if you want to use '.'
return self.__root__.__getattribute__(item) |
Sure, something like that might help if you want to use the root class transparently: def use_root_class_decorator(cls):
def new_method_proxy(func):
def inner(self, *args):
return func(self.__root__, *args)
return inner
cls.__getattr__ = new_method_proxy(getattr)
cls.__setattr__ = new_method_proxy(setattr)
cls.__delattr__ = new_method_proxy(delattr)
cls.__getattr__ = new_method_proxy(getattr)
cls.__bytes__ = new_method_proxy(bytes)
cls.__str__ = new_method_proxy(str)
cls.__bool__ = new_method_proxy(bool)
cls.__dir__ = new_method_proxy(dir)
cls.__hash__ = new_method_proxy(hash)
# not sure of the consequences of using / not using the following line...
# cls.__class__ = property(new_method_proxy(operator.attrgetter("__class__")))
cls.__eq__ = new_method_proxy(operator.eq)
cls.__lt__ = new_method_proxy(operator.lt)
cls.__gt__ = new_method_proxy(operator.gt)
cls.__ne__ = new_method_proxy(operator.ne)
cls.__hash__ = new_method_proxy(hash)
cls.__getitem__ = new_method_proxy(operator.getitem)
cls.__setitem__ = new_method_proxy(operator.setitem)
cls.__delitem__ = new_method_proxy(operator.delitem)
cls.__iter__ = new_method_proxy(iter)
cls.__len__ = new_method_proxy(len)
cls.__contains__ = new_method_proxy(operator.contains)
return cls
@use_root_class_decorator
class Animal(BaseModel):
__root__: Annotated[
Union[
Dog,
Bird
],
Field(discriminator='animal_type')
] Or if you can, you may use this lib: https://github.com/GrahamDumpleton/wrapt |
Thanks for the patience, I've submitted a fix for this, see #4071. @adrienyhuel, please could you take a look and test that it fixes your problem. I hope to get v1.9.1 out with this included in the next few days. |
@samuelcolvin Thanks for the fix, waiting for 1.9.1 release :) |
Thanks so much for confirming. Should be very soon now. |
Checks
Bug
Output of
python -c "import pydantic.utils; print(pydantic.utils.version_info())"
:I tested the ne feature "discriminated unions", using the sample at https://pydantic-docs.helpmanual.io/usage/types/#discriminated-unions-aka-tagged-unions, and it works great.
But when I use it in arrays, it doesn't work. The resulting json schema doesn't contains any discriminator.
See here, I modified the sample ( pet: Pet to pet: List[Pet] ) :
JSON schema gives :
The text was updated successfully, but these errors were encountered: