Skip to content
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

Discriminated union does not work with aliased literal field #3849

Closed
3 tasks done
ljnsn opened this issue Feb 22, 2022 · 8 comments
Closed
3 tasks done

Discriminated union does not work with aliased literal field #3849

ljnsn opened this issue Feb 22, 2022 · 8 comments
Assignees
Labels
bug V1 Bug related to Pydantic V1.X

Comments

@ljnsn
Copy link

ljnsn commented Feb 22, 2022

Checks

  • I added a descriptive title to this issue
  • I have searched (google, github) for similar issues and couldn't find anything
  • I have read and followed the docs and still think this is a bug

Bug

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

             pydantic version: 1.9.0
            pydantic compiled: True
                 install path: /home/ljnsn/.virtualenvs/py39/lib/python3.9/site-packages/
pydantic
               python version: 3.9.7 (default, Sep 10 2021, 14:59:43)  [GCC 11.2.0]
                     platform: Linux-5.15.23-76051523-generic-x86_64-with-glibc2.34
     optional deps. installed: ['typing-extensions']

It seems like the discriminator field for a discriminated union is only recognized when it is passed by its alias, even when allow_population_by_field_name = True. I would expect to be able to pass the discriminator field by field name, just like any other field, when that config option is set.

from typing import Literal, Union

from pydantic import BaseModel, Field


class Base(BaseModel):
    class Config:
        allow_population_by_field_name = True


class Cat(Base):
    pet_type: Literal["cat"] = Field(alias="petType")
    meows: int


class Dog(Base):
    pet_type: Literal["dog"] = Field(alias="petType")
    barks: float


class Lizard(Base):
    pet_type: Literal["reptile", "lizard"] = Field(alias="petType")
    scales: bool


class Model(Base):
    pet: Union[Cat, Dog, Lizard] = Field(..., discriminator="pet_type")
    n: int


print(Dog(**{"pet_type": "dog", "barks": 3.14}))

print(Model(pet={"petType": "dog", "barks": 3.14}, n=1))

print(Model(pet={"pet_type": "dog", "barks": 3.14}, n=1))
# > pydantic.error_wrappers.ValidationError: 1 validation error for Model pet
#   Discriminator 'pet_type' is missing in value (type=value_error.discriminated_union.miss
#   ing_discriminator; discriminator_key=pet_type)
@ljnsn ljnsn added the bug V1 Bug related to Pydantic V1.X label Feb 22, 2022
@ljnsn
Copy link
Author

ljnsn commented Feb 22, 2022

I think this is a duplicate of #3846. Apologies, I started writing this yesterday evening before the other issue was created.

@chornsby
Copy link
Contributor

Spooky that we found a similar problem around the same time! 😅

If you'd like to confirm, you could try the tentative fix that I've proposed here to see if it fixes your issue too. If so then we could link both tickets to the same PR.

#3847

@ljnsn
Copy link
Author

ljnsn commented Feb 22, 2022

I'll check this tomorrow at work, thanks!

EDIT:

Just tried it out and it doesn't seem to work for the example above 😞

---> 35 print(Model(pet={"pet_type": "dog", "barks": 3.14}, n=1))

~/src/pydantic/pydantic/main.py in __init__(__pydantic_self__, **data)
    329         values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data)
    330         if validation_error:
--> 331             raise validation_error
    332         try:
    333             object_setattr(__pydantic_self__, '__dict__', values)

ValidationError: 1 validation error for Model
pet
  Discriminator 'pet_type' is missing in value (type=value_error.discriminated_union.missing_discriminator; discriminator_key=pet_type)

@mojimi
Copy link

mojimi commented Feb 25, 2022

Can confirm, just had this same issue today

@alon-loris
Copy link

Hello, we are having the same issue.
It happens not only when passing dicts like the OP did, but also if you pass model instances (See example below).
@samuelcolvin Is there any plan to fix this soon?
Thank you

class Cat(BaseModel):
    pet_type: Literal['cat'] = Field(alias='pet_type_alias')
    name: str

class Dog(BaseModel):
    pet_type: Literal['dog'] = Field(alias='pet_type_alias')
    name: str

class Home(BaseModel):
    pet: Annotated[Union[Dog, Cat], Field(discriminator='pet_type')]

cat = Cat(name="Mitzi", pet_type_alias='cat')
home = Home(pet=cat)

# pydantic.error_wrappers.ValidationError: 1 validation error for Home
# pet
# Discriminator 'pet_type' is missing in value (type=value_error.discriminated_union.missing_discriminator; discriminator_key=pet_type)

@samuelcolvin
Copy link
Member

Yes, it's fixed in pydantic-core and will be fixed in V2.

@samuelcolvin samuelcolvin added this to the Version 2 milestone Aug 30, 2022
@adriangb adriangb assigned adriangb and dmontagu and unassigned adriangb Apr 28, 2023
@dmontagu
Copy link
Contributor

It seems this is fixed in v2:

from pydantic import BaseModel, Field
from typing import Union
from typing_extensions import Literal, Annotated
class Cat(BaseModel):
    pet_type: Literal['cat'] = Field(alias='pet_type_alias')
    name: str

class Dog(BaseModel):
    pet_type: Literal['dog'] = Field(alias='pet_type_alias')
    name: str

class Home(BaseModel):
    pet: Annotated[Union[Dog, Cat], Field(discriminator='pet_type')]

cat = Cat(name="Mitzi", pet_type_alias='cat')
home = Home(pet=cat)
print(home)
#> pet=Cat(pet_type='cat', name='Mitzi')

@dmontagu
Copy link
Contributor

This should be fixed now in the just-released 1.10.8, which should be available through PyPI shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V1 Bug related to Pydantic V1.X
Projects
None yet
Development

No branches or pull requests

7 participants