How to access a python dictionary keys as pydantic model fields - python

I am not able to figure out how I can access a dictionary keys using pydantic model properties instead of using get directly on the dictionary.
from pydantic import BaseModel
class Person(BaseModel):
name: str
age: int
def some_function(data: Person):
abc=data.name
print(abc)
person={'name':'tom','age':12}
some_function(person)
I get : AttributeError: 'dict' object has no attribute 'name'
Basically, I need to pass a dict to a function which will be received as a Pydantic Model type and then I want to be able to use . (dot) operator to access the contents of the passed dict. I have seen something similar implemented but I have no idea why it is not working for me.
Is this possible ?

You need to convert your dict to the pydantic class first,
some_function(Person(**person))
or
some_function(Person.parse_obj(person))

Related

Cannot determine if type of field in a Pydantic model is of type List

I am trying to automatically convert a Pydantic model to a DB schema. To do that, I am recursively looping through a Pydantic model's fields to determine the type of field.
As an example, I have this simple model:
from typing import List
from pydantic import BaseModel
class TestModel(BaseModel):
tags: List[str]
I am recursing through the model using the __fields__ property as described here: https://docs.pydantic.dev/usage/models/#model-properties
If I do type(TestModel).__fields__['tags'] I see:
ModelField(name='tags', type=List[str], required=True)
I want to programatically check if the ModelField type has a List origin. I have tried the following, and none of them work:
type(TestModel).__fields__['tags'].type_ is List[str]
type(TestModel).__fields__['tags'].type_ == List[str]
typing.get_origin(type(TestModel).__fields__['tags'].type_) is List
typing.get_origin(type(TestModel).__fields__['tags'].type_) == List
Frustratingly, this does return True:
type(TestModel).__fields__['tags'].type_ is str
What is the correct way for me to confirm a field is a List type?
Pydantic has the concept of the shape of a field. These shapes are encoded as integers and available as constants in the fields module. The more-or-less standard types have been accommodated there already. If a field was annotated with list[T], then the shape attribute of the field will be SHAPE_LIST and the type_ will be T.
The type_ refers to the element type in the context of everything that is not SHAPE_SINGLETON, i.e. with container-like types. This is why you get str in your example.
Thus for something as simple as list, you can simply check the shape against that constant:
from pydantic import BaseModel
from pydantic.fields import SHAPE_LIST
class TestModel(BaseModel):
tags: list[str]
other: tuple[str]
tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]
assert tags_field.shape == SHAPE_LIST
assert other_field.shape != SHAPE_LIST
If you want more insight into the actual annotation of the field, that is stored in the annotation attribute of the field. With that you should be able to do all the typing related analyses like get_origin.
That means another way of accomplishing your check would be this:
from typing import get_origin
from pydantic import BaseModel
class TestModel(BaseModel):
tags: list[str]
other: tuple[str]
tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]
assert get_origin(tags_field.annotation) is list
assert get_origin(other_field.annotation) is tuple
Sadly, neither of those attributes are officially documented anywhere as far as I know, but the beauty of open-source is that we can just check ourselves. Neither the attributes nor the shape constants are obfuscated, protected or made private in any of the usual ways, so I'll assume these are stable (at least until Pydantic v2 drops).

Pydantic field does not take value

While attempting to name a Pydantic field schema, I received the following error:
NameError: Field name "schema" shadows a BaseModel attribute; use a different field name with "alias='schema'".
Following the documentation, I attempted to use an alias to avoid the clash. See code below:
from pydantic import StrictStr, Field
from pydantic.main import BaseModel
class CreateStreamPayload(BaseModel):
name: StrictStr
_schema: dict[str: str] = Field(alias='schema')
Upon trying to instantiate CreateStreamPayload in the following way:
a = CreateStreamPayload(name= "joe",
_schema= {"name": "a name"})
The resulting instance has only a value for name, nothing else.
a.dict()
{'name': 'joe'}
This makes absolutely no sense to me, can someone please explain what is happening?
Many thanks
From the documentation:
Class variables which begin with an underscore and attributes annotated with typing.ClassVar will be automatically excluded from the model.
In general, append an underscore to avoid conflicts as leading underscores are seen as either dunder (magic) members or private members: _schema ➡ schema_

Is it possible to get name of pydantic field as string?

class Settings(BaseSettings):
SITE_URL: str
CONFIG = Settings()
>>> CONFIG.SITE_URL
returns str, and that's expected
Is it possible somehow to get access to dotted string representation of field?
CONFIG.SITE_URL.__some_magic_attr_ == 'CONFIG.SITE_URL'
Once initialized, the attribute of a Pydantic model is simply of the type that was defined for it. In this case SITE_URL is just a string. Thus, there is no special magic method, to get its field name.
Depending on your actual use case though, the __fields__ attribute of the model might be useful. It is a dictionary mapping field names to the ModelField objects. For example
from pydantic import BaseSettings
class Settings(BaseSettings):
SITE_URL: str
print(Settings.__fields__['SITE_URL'])
gives
name='SITE_URL' type=str required=True
If you have your settings object, you can for example do this:
for name in CONFIG.__fields__.keys():
print(f'{CONFIG.__class__.__name__}.{name}')
giving you
Settings.SITE_URL
...
If you want the name of the variable storing your settings object, I assume you can just write it as a string manually, i.e.
for name in CONFIG.__fields__.keys():
print(f'CONFIG.{name}')

A way to set field validation attribute in pydantic

I have the following pydentic dataclass
#dataclass
class LocationPolygon:
type: int
coordinates: list[list[list[float]]]
this is taken from a json schema where the most inner array has maxItems=2, minItems=2.
I couldn't find a way to set a validation for this in pydantic.
setting this in the field is working only on the outer level of the list.
#dataclass
class LocationPolygon:
type: int
coordinates: list[list[list[float]]] = Field(maxItems=2, minItems=2)
using #validator and updating the field attribute doesn't help either as the value was already set and basic validations were already made:
#validator('coordinates')
def coordinates_come_in_pair(cls, values, field):
field.sub_fields[0].sub_fields[0].field_info.min_items = 2
field.sub_fields[0].sub_fields[0].field_info.max_items = 2
I thought about using root_validator with pre=True, but there are only the raw values there.
Is there a way to tweak the field validation attributes or use pydantic basic rules to make that validation?
You can use conlist function to create nested constrained list:
from pydantic import conlist
from pydantic.dataclasses import dataclass
#dataclass
class LocationPolygon:
type: int
coordinates: list[list[conlist(float, min_items=2, max_items=2)]]

Define CQLEngine model dynamically using 'type'

I am using Datastax Cassandra python driver's Object Mapper for defining cassandra table columns at run time (requirements are like those).
Table and column name and column types are resolved at run time.
I am trying to define a cassandra cqlengine model at runtime using 'type' to define a class.
Looks like Model class defined in python driver has added a metaclass to Model
#six.add_metaclass(ModelMetaClass)
class Model(BaseModel):
...
Is there even a way to define Models using type?
I am seeing following error while defining a Model class
from cassandra.cqlengine.models import Model
from cassandra.cqlengine import columns as Columns
attributes_dict = {
'test_id': Columns.Text(primary_key=True)
'test_col1': Columns.Text()
}
RunTimeModel = type ('NewModelName', tuple(Model), attributes_dict)
Error:
RunTimeModel = type ('NewModelName', tuple(Model), attributes_dict)
TypeError: 'ModelMetaClass' object is not iterable
I'll stay away from the rest, but to answer the question about the error, I think you have a simple syntax error trying to construct a tuple from a non-sequence argument. Instead, you might use the tuple literal notation:
RunTimeModel = type ('NewModelName', (Model,), attributes_dict)

Categories

Resources