Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":
pydantic version: 1.6.1
pydantic compiled: True
install path: /Users/user/virtualenvs/testp-wbcFebeE-py3.8/lib/python3.8/site-packages/pydantic
python version: 3.8.5 (default, Aug 10 2020, 10:25:56) [Clang 11.0.0 (clang-1100.0.20.17)]
platform: macOS-10.14.6-x86_64-i386-64bit
optional deps. installed: ['email-validator']
Hi, I'm writing a slack application which has multiple types which have the same fields and the only difference is the content of the field: https://api.slack.com/types/user
E.g: Difference between channel ID and user ID(A): CJ03ECZLG X UJ03ECZLG (user starts with U)
And for some reason Pydantic casts Channel (C) to the User (U) based on the order of types inside the annotation.
Am I using pydantic wrong or is the a legitimate bug?
If there is a better approach or workaround it would be much appreciated.
Thank you.
from pydantic import validator, BaseModel
from typing import Optional, Union, List
class U(BaseModel):
id: str
mention: Optional[str]
@validator("mention", pre=True)
def resolve_mention(cls, _, values):
if 'id' in values:
return f"<@{values['id']}>"
class B(BaseModel):
name: str
mention: Optional[str]
@validator("mention", pre=True)
def resolve_mention(cls, _, values):
if 'name' in values:
return f"{values['name']}"
class C(BaseModel):
id: str
mention: Optional[str]
@validator("mention", pre=True)
def resolve_mention(cls, _, values):
if 'id' in values:
return f"<#{values['id']}>"
class Final(BaseModel):
# This does not work, why?
entities: List[
Union[
U,
B,
C, # C is casted to U
]
]
print(
Final(
entities=[
U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'),
B(name='someone else', mention='someone else')
]
)
)
print(
Final(
entities=[
U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'),
B(name='someone else', mention='someone else'),
C(id='CJ03ECZLG', mention='<@CJ03ECZLG>'), # This is casted to U
]
)
)
Actual result
entities=[U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'), B(name='someone else', mention='someone else')]
entities=[U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'), B(name='someone else', mention='someone else'), U(id='CJ03ECZLG', mention='<@CJ03ECZLG>')]
Expected result:
entities=[U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'), B(name='someone else', mention='someone else')]
entities=[U(id='UJ03ECZLG', mention='<@UJ03ECZLG>'), B(name='someone else', mention='someone else'), C(id='CJ03ECZLG', mention='<@CJ03ECZLG>')]
Hello @1oglop1
It's been a know issue for quite a long time now (last issue with same problem: https://github.com/samuelcolvin/pydantic/issues/2079)
_pydantic_ tries to coerce in the order of the union and your C instance can be coerced as U hence the result.
You can add a dumb Literal for each model (a bit like you would do with TypeScript to discrimate unions).
You can also have a look at #2092, which solves your problem (just need to replace Union by StrictUnion). Feedback is more than welcome on this PR :)
Cheers
Oh, thank you for point me in the right direction. I've been searching for possible duplicates but could not find any.
Most helpful comment
Hello @1oglop1
It's been a know issue for quite a long time now (last issue with same problem: https://github.com/samuelcolvin/pydantic/issues/2079)
_pydantic_ tries to coerce in the order of the union and your
Cinstance can be coerced asUhence the result.You can add a dumb
Literalfor each model (a bit like you would do with TypeScript to discrimate unions).You can also have a look at #2092, which solves your problem (just need to replace
UnionbyStrictUnion). Feedback is more than welcome on this PR :)Cheers