Pydantic: "from" keyword in data to validate raises an Syntax error

Created on 9 Apr 2018  路  12Comments  路  Source: samuelcolvin/pydantic

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:

  1. validating the data
  2. serialising the model

Thanks fo your help

documentation question

Most helpful comment

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_))

All 12 comments

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 d

Would 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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sommd picture sommd  路  3Comments

ashpreetbedi picture ashpreetbedi  路  3Comments

timonbimon picture timonbimon  路  3Comments

nav picture nav  路  3Comments

cdeil picture cdeil  路  3Comments