I'm building an application using REST APIs for a 3rd party application. When I retrieve an object from the API it sends me a json
which I pass into a class that internally takes out keys and provides the data via the class using property
Example:
Payload:
{
"value":{
"boot":{
"delay":10,
"legacy_boot":false
}
}
}
class AbstractInfo:
def __init__(self, json):
try:
self.info = json["value"]
except KeyError:
# KeyError is raised when nested dictionary is passed which does not contain `value` key
self.info = json
def to_dict(self):
return self.info
class BootInfo(AbstractInfo):
@property
def delay(self):
return self.info["delay"]
@property
def legacy_boot(self):
return self.info["legacy_boot"]
class MainInfo(AbstractInfo):
@property
def boot(self):
return BootInfo(json=self.info["boot"])
Now, from some other process, I call the MainInfo
class using dynamic imports and do something like this
# Do rest API call and get the payload in a variable
MainInfo(json=payload)
This all works well till the time, I'm only trying to fetch the values from the payload like so
# GetInfo does dynamic import for the correct class based on the resource_id
info = GetInfo("resource_id")
print(info.boot.delay)
Now, my objective is to create an object using the schema defined in BootInfo
so that it returns the relevant dictionary
.
Here are results of print statements to help clear things up
print(MainInfo(json=json).to_dict())
{'boot': {'delay': 10, 'legacy_boot': False}}
print(MainInfo(json=json).boot.to_dict())
{'delay': 10, 'legacy_boot': False}
I would like to do something like
bi = BootInfo(delay=20, legacy_boot=True).build_dict()
print(bi)
{'delay': 20, 'legacy_boot': True}
# Then use `bi` into another place to mount the dictionary easily like building a lego set
I'm aware that my BootInfo
does not accept delay
and legacy_boot
as the argument. If I add them, then how do I set the values when the data arrives via json
? I feel like I'm in a chicken and egg situation here. And, if possible, I would like to keep the current structure of the code as is so that it's less work to find the keys from the nested structure. The more nested structure I have, the more classes I'm making. I'm also doing validation under properties when a certain key is dependent on the value from another property and might not be available otherwise in the json payload.
I have looked into getter
and setter
and tried a few things and had several issues and just could not make my requirements work.
Does anyone know the best way to get this done or show me the right direction with a sample code?
I think this is a prime example on when to use a validator library. (personal favorite: https://pydantic-docs.helpmanual.io/)
Advantages:
Example:
from pydantic import BaseModel
class BootInfo(BaseModel):
delay: int
legacy_boot: bool
class MainInfo(BaseModel):
boot: BootInfo
class Info(BaseModel):
value: MainInfo
then you can do:
payload = {
"value":{
"boot":{
"delay":10,
"legacy_boot":false
}
}
}
info = Info(**data)
info.value.boot.delay
info.value.boot.legacy_boot
assert info.dict() == payload
# or init the class by properties
boot_info = BootInfo(delay=10, legacy_boot=False)
assert boot_info.delay == 10
boot_info.dict()