How to use a reserved keyword in pydantic model - python

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)

Related

How to test a BaseModel class with fields retrieved from a method?

I find myself writing tests for methods for two classes, one of which has str fields that it retrieves from another BaseModel class:
class Person(BaseModel):
firstname: Optional[str] = settings.get_settings().firstname
lastname: Optional[str] = settings.get_settings().lastname
Now, when I try to create the fixture I get this problem
#test file
#pytest.fixture
def person_class():
person = Person(
firstname="Marco",
lastname="Marche"
)
return person()
Error:
pydantic.error_wrappers.ValidationError: 31 validation errors for Settings
Settings is a BaseSettings class.
class Settings(BaseSettings):
firstname: str,
lastname: str,
address: str,
telephone: str
#lru_cache
def get_settings() -> Settings:
return Settings()
How can I solve it by creating a "fake" Person class with fields entered by me?
I'm assuming that you have a typo in your Settings definition and there should be no commas in the firstname, lastname, and address lines.
The error you're getting is most likely because your Settings class requires a value for firstname, lastname, and the other fields but you didn't provide any.
I think the easiest solution would be to give it some fake values in the instantiation in your get_settings() function, e.g.:
#lru_cache
def get_settings() -> Settings:
return Settings(
firstname="fake firstname",
lastname="fake lastname",
address="fake address",
telephone="fake telephone",
)
Pydantic does provide the construct() method on the Models which allows to create models without validation, however, I don't think this would work in your situation because you are getting the default values for Person from Settings which, in a case like yours, is still being evaluated.

how to validate keys with whitespaces in pydantic

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

Pydantic - Use schema field as field type in another schema

Is this concept somehow possible in Pydantic:
## one file
class StatusOut(BaseModel):
id: int
name: str
## another file
class UserOut(BaseModel):
name: str
status_name: StatusOut.name ## IS THIS ACHIEVABLE???
I get an error "AttributeError: type object 'StatusOut' has no attribute 'name'"
So the first and only thing I got in mind is to override validation with #validator(...) decorator.
from pydantic import BaseModel, validator
class StatusOut(BaseModel):
id: int
name: str
class UserOut(BaseModel):
name: str
status_name: StatusOut
#validator("status_name")
def redefine_status_name(cls, v):
return v.name
status = StatusOut(id=1, name="status")
user = UserOut(name="test", status_name=status)
print(user.json()) # {"name": "test", "status_name": "status"}
I guess there is no need to explain what I did, I just returned StatusOut().name instead of StatusOut itself
Although I do not suggest doing that, as it makes status_name's real type not the one that is declared and might confuse you in future

SqlModel : Fastapi AttributeError: type object 'AddressBaseCore' has no attribute '__config__'

I am new to fastapi and SQLModel, i was trying to implement some basic code from my existing lib, I have an Address Class
like
#dataclass
class Address(DataClassJsonMixin):
addr1: str
city: str
province: str
I simply want to create a class in SQLModel that connects to DB. I have only added a new column ID here. i am getting below error where i am not sure why is it asking for a config attribute.
class AddressMaster(SQLModel, Address):
id: int = Field(default=None, primary_key=True)
AttributeError: type object 'Address' has no attribute '__config__'
It's failing on config = getattr(base, "__config__") that has some information which I am not able to comprehand.
# Only one of the base classes (or the current one) should be a table model
# this allows FastAPI cloning a SQLModel for the response_model without
# trying to create a new SQLAlchemy, for a new table, with the same name, that
# triggers an error
try 1:
from sqlmodel import SQLModel, Field
from ...core import Address
from dataclasses import dataclass
#dataclass
class AddressDB(Address, SQLModel):
pass
# END AddressDB
class AddressMaster(AddressDB, table=True):
"""
Address Master Table
"""
id: int = Field(default=None, primary_key=True)
# END AddressMaster
Object Creation
objAd = AddressMaster.from_dict({"addr1": "Kashmir", "city": "Srinagar", "province": "Kashmir"})
There is an error in the semantics of your AdressMaster class.
If it is meant to be a class related to your DB. Then you have to specify in the first parameter either the class inheriting from a SQL model or from SQLmodel (And in this case, you should rewrite each attribute of your model within this class) directly. And it is necessary to pass it the argument table=True
class AddressMaster(Address, table=True):
id: int = Field(default=None, primary_key=True)
# Here the attributes will be inherited from your Adress class
# (provided that this one in its parentage is an inheritance link with a modelSQL)
Or
class AddressMaster(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
addr1: str
city: str
province: str
# Here, the class is independent from the other pydantic
# validation models since it inherits directly from SQLModel
In Try 1:
You are trying to pass two parameters to your AddressDB class, one of which is an SQLModel. However, SQLModel allows to override SQLAlchemy, and accepts as parameter only models from SQLAlchemy or Pydantic. During the initialization, it goes through the arguments passed in parameter and tries to call the method or attribute Config which exists in the pydantic and SQLAlchemy models. This is the source of your error since you pass in parameter a DataClassJsonMixin which has no Config method or attribute. This is the origin of your error.
How to solve it. You just have to not call DataClassJsonMixin which seems to me to encode / decode JSON data. However, this is a basic behavior of Pydantic (which is used behind SQLModel).
So if you use the first method shown above (i.e. inherited from a SQLModel), you just have to put your validation fields inside AddressDB and make this class inherit only from SQLModel
class AddressDB(SQLModel):
addr1: str
city: str
province: str

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

Categories

Resources