Hello,
I'm running into a problem when validating the following data with pydantic:
{
"validity_dates": {
"from": "2014-12-31",
"to": "2015-12-31"
}
}
It's the "from" key in the data that is problematic:
class ValidityDates(pydantic.BaseModel):
from: str
to: str
This code raises a SyntaxError: invalid syntax. How can I map a supported class attribute like "from_" to the dict key "from" when:
Thanks fo your help
from datetime import date
from pydantic import BaseModel
class Foobar(BaseModel):
from_: date
class Config:
fields = {
'from_': 'from'
}
foobar = Foobar(**{'from': '2014-12-31'})
print(foobar)
print(repr(foobar.from_))
closing this, but feel free to comment if that doesn't solve your problem.
Thanks for the fast answer. To have "form" as a keyword when serialising, I overwrote the "dict()" method as follows. Is there another way?
class ValidityDates(pydantic.BaseModel):
class Config:
fields = {
'from_': 'from'
}
def dict(self, *, include=None, exclude=set()):
d = dict()
for k, v in self:
if k not in exclude and (not include or k in include):
if k == "from_":
d["from"] = v
else:
d[k] = v
return d
from_: str
to: str
No problem, surely
def dict(self, *args, **kwargs):
d = super().dict(*args, **kwargs)
d['from'] = d.pop('from_')
return d
Would be simpler.
Way better, thanks!
I'd like to add this in documentation in section "How to handle JSON which contains fields reserved as keywords in Python" or similar. Because this special case happened to be not so special.
For example, public Telegram Bot API has this from field. Stackoverflow question about this for reference
Yes please, PR welcome.
No problem, surely
def dict(self, *args, **kwargs): d = super().dict(*args, **kwargs) d['from'] = d.pop('from_') return dWould be simpler.
Note, this is no longer necessary, since dict() now has the by_alias argument.
For folks not using .dict(by_alias=True) , use the allow_population_by_field_name = True property (earlier was called allow_population_by_alias) in your Pydantic model: Source. So for the above code-sample, your model would look like:
class Foobar(BaseModel):
from_: date
class Config:
allow_population_by_field_name = True
fields = {
'from_': 'from'
}
I don't understand the question.
Just building on your previous answer @samuelcolvin. I'm using FastAPI + Pydantic, so the above solution worked for me. Using by_alias was one option, but that would mean instead of returning a Pydantic model to FastAPI to interpret, I always had to resort to returning a dict with .dict(by_alias=True), which made things a bit more granular and inelegant. Instead, preferred just returning the Pydantic model/schema object as responses to FastAPI routes. As a result, just specifying allow_population_by_field_name=True allowed me to always use aliases, enforced right in the Pydantic model and so I don't have to repeat .dict(by_alias=True) everytime I created an object of Foobar
So my FastAPI routes now look like:
@router.post(
'/foobar',
response_model=Foobar
)
async def foobar(
from_: int = Query(None, title="unix timestamp", gt=0)
):
return Foobar(from_=from_)
@router.post(
'/foobar2',
response_model=Foobar
)
async def foobar2(
from_: int = Query(None, title="unix timestamp", gt=0)
):
return Foobar(from_=from_)
with model as:
class Foobar(BaseModel):
from_: int
class Config:
allow_population_by_field_name = True
fields = {
'from_': 'from'
}
Earlier the routes were like:
@router.post(
'/foobar',
response_model=Foobar
)
async def foobar(
from_: int = Query(None, title="unix timestamp", gt=0)
):
return Foobar(from_=from).dict(by_alias=True)
@router.post(
'/foobar2',
response_model=Foobar
)
async def foobar2(
from_: int = Query(None, title="unix timestamp", gt=0)
):
return Foobar(from_=from).dict(by_alias=True)
Hi @samuelcolvin, first of all thanks for this great project!
I had a question regarding your following response
from datetime import date from pydantic import BaseModel class Foobar(BaseModel): from_: date class Config: fields = { 'from_': 'from' } foobar = Foobar(**{'from': '2014-12-31'}) print(foobar) print(repr(foobar.from_))
While it seems this approach works when inheriting the BaseModel class, I can't quite seem to get the same behavior when using the pydantic.dataclasses.dataclass decorator approach.
For example:
from datetime import date
from pydantic import BaseModel
class Foo(BaseModel):
from_: date
to: date
class Config:
fields = {'from_': 'from'}
foo = Foo(**{'from': date(2019, 1, 1), 'to': date(2019, 2, 1)})
Works as expected (as outlined in your response above). However, when we try the equivalent approach using dataclass, I get the following error:
from datetime import date
from pydantic.dataclasses import dataclass
class Config:
fields = {'from_': 'from'}
@dataclass(config=Config)
class Bar(BaseModel):
from_: date
to: date
bar = Bar(**{'from': date(2019, 1, 1), 'to': date(2019, 2, 1)})
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-91-76a342e8b722> in <module>
----> 1 bar = Bar(**{'from': date(2019, 1, 1), 'to': date(2019, 2, 1)})
TypeError: __init__() got an unexpected keyword argument 'from'
The usage came from the model_config documentation. Am I doing something wrong here?
Thanks in advance for your time and help!
Most helpful comment