how to validate keys with whitespaces in pydantic - python

I have a json key with whitespace in it:
My_dict = {"my key": 1}
I want to create a Model to model it:
from pydantic import BaseModel
class MyModel(BaseModel):
mykey: int
# my key isn't a legit variable name
# my_key is, but like mykey - it doesn't catch the correct key from the json
MyModel(**my_dict)
This doesn't work.
I tried playing with the BaseModel.Config, but didn't get anywhere. Didn't see anything on the docs as well.
Is this possible?
I can use a workaround: Go over the json, replace all key's whitespaces into underscores, and then use pydantic but I would love to not use this...

Yes, it's possible by using Field's aliases:
from pydantic import BaseModel, Field
class MyModel(BaseModel):
mykey: int = Field(alias='my key')
class Config:
allow_population_by_field_name = True
print(MyModel(**{"my key": 1}))
print(MyModel(**{"mykey": 1}))

Related

How to use a reserved keyword in pydantic model

I need to create a schema but it has a column called global, and when I try to write this, I got an error.
class User(BaseModel):
id:int
global:bool
I try to use another name, but gives another error when try to save in db.
It looks like you are using the pydantic module. You can't use the name global because it's a reserved keyword so you need to use this trick to convert it.
class User(BaseModel):
id: int
global_: bool
class Config:
fields = {
'global_': 'global'
}
or
class User(BaseModel):
id: int
global_: bool = Field(..., alias='global')
To create a class you have to use a dictionary (because User(id=1, global=False) also throws an error:
user = User(**{'id': 1, 'global': False})
To get data in correct schema use by_alias:
user.dict(by_alias=True)

How can I unpack a Pydantic BaseModel into kwargs?

I am trying to make a function that takes a pydantic BaseModel as an input to run another function. I need to unpack the BaseModel into kwargs. I tried doing this:
def run_routing_from_job(job):
return run_routing(
job.inputs.input_file,
**job.inputs.config.dict()
)
where job is of the format
class Job(BaseModel):
client_info: ClientInfo # Another BaseModel
inputs: RoutingJobInputs # Another BaseModel
uid: UUID = Field(default_factory=uuid4)
status: str = "job_queued"
result: int = None
However, doing .dict() parses all of the items recursively into a dictionary format. I want to keep the client_info and inputs as a BaseModel class, not convert it into a dictionary.
I could make a way to do it, but I can't find a clean way to do it.
I worked it out, just replace .dict() with __dict__
def run_routing_from_job(job):
return run_routing(
job.inputs.input_file,
**job.inputs.config.__dict__
)

Assigning Pydantic Fields not by alias

How can I create a pydantic object, without useing alias names?
from pydantic import BaseModel, Field
class Params(BaseModel):
var_name: int = Field(alias='var_alias')
Params(var_alias=5) # works
Params(var_name=5) # does not work
You need to use allow_population_by_field_name model config option, which is False by default.
from pydantic import BaseModel, Field
class Params(BaseModel):
var_name: int = Field(alias='var_alias')
class Config:
allow_population_by_field_name = True
Params(var_alias=5) # works
Params(var_name=5) # works

Generate Literal in Runtime

I want to create a pydantic Model with a Literal field, but the options should be derived from a list. Something like this:
from pydantic import BaseModel
from typing import Literal
opts=['foo','bar']
class MyModel(BaseModel):
select_one : Literal[opts]
Is there some way this could be solved by enumeration?
Yes, there is. If you mix in str with enum.Enum, you'll get an enumeration with members that are fully backwards-compatible with strings (can be used anywhere a str type is expected) and are type-checker friendly.
from enum import Enum
from pydantic import BaseModel
class Options(str, Enum):
FOO = 'foo'
BAR = 'bar'
class MyModel(BaseModel):
select_one : Options
In order for this to work, the Literal needs to be over a tuple (see also here).
There are several options, depending on your situation:
from pydantic import BaseModel
from typing import Literal
# First option:
class MyModel(BaseModel):
select_one: Literal['foo', 'bar']
# Second option:
opts_tuple = ('foo', 'bar')
class MyModel(BaseModel):
select_one: Literal[opts_tuple]
# Third option:
opts_list = ['foo', 'bar']
class MyModel(BaseModel):
select_one: Literal[tuple(opts_list)]

Is it possible to modify Pydantic BaseModel attributes just after creating it?

I am starting to learn FastAPI and Pydantic and have a doubt. I have the following subclass of BaseModel
class Product(BaseModel):
image: str
name: str
After saving this model, I want image to store the value /static/ + image so as to create nice hyperlinked REST endpoint. This is possible using __post_init_post_parse__ hook of pydantic dataclass but since FastAPI currently doesn't support it, I was wondering what can be a workaround this.
You could use a custom validator:
>>> from pydantic import BaseModel, validator
>>> class Product(BaseModel):
image: str
name: str
#validator('image')
def static_mage(cls, image):
return '/static/{}'.format(image)
>>> p = Product(image='pic.png', name='product_1')
>>> p
Product(image='/static/pic.png', name='product_1')

Categories

Resources