I have a small python example using pydantic that shows my issue:
from typing import Literal
from pydantic import BaseModel, Field, ConfigDict
class Base(BaseModel):
# This method allows inherited classes to be subscriptable like a dictionary i.e value = class['key']
def __getitem__(self, item):
return getattr(self, item)
model_config = ConfigDict(extra='forbid')
class Foo(Base):
bar: Literal['bar1', 'bar2'] = Field(default=None, frozen=True, alias="bar_path")
a = {'bar': 'bar1'}
b = Foo(**a)
This causes the following exception from pydantic:
Traceback (most recent call last):
File "/home/hawk/applications/pydantic_example.py", line 16, in <module>
b = Foo(**a)
^^^^^^^^
File "/home/hawk/miniconda3/envs/bird/lib/python3.11/site-packages/pydantic/main.py", line 164, in __init__
__pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Foo
bar
Extra inputs are not permitted [type=extra_forbidden, input_value='bar1', input_type=str]
For further information visit https://errors.pydantic.dev/2.5/v/extra_forbidden
If i remove the alias then it works fine. If i have the alias but change it to
a = {'bar_path': 'bar1'}
then it works. Any idea why the alias is being treated as an extra parameter?
I think Pydantic v2 requires explicitly defining the choice of aliases, using the AliasChoice
class. See the following example:
from typing import Literal
from pydantic import BaseModel, Field, ConfigDict, AliasChoices
class Base(BaseModel):
def __getitem__(self, item):
return getattr(self, item)
model_config = ConfigDict(extra='forbid')
class Foo(Base):
bar: Literal['bar1', 'bar2'] = Field(default=None, frozen=True, alias=AliasChoices('bar', 'bar_path'))
a = {'bar': 'bar1'}
b = Foo(**a)
print(b)
c = Foo(bar_path='bar2')
print(c)
Which outputs:
bar='bar1'
bar='bar2'
This makes both names work. My initial expectation was the field name would always work as well, but it seems that is not the case.
I hope this helps!