Compare Extra fields with Model attributes using the same types

Viewed 19

I created a pydantic model that accepts extra fields and store them in an internal dict.
The model has a validator to compare these extra fields with the model attributes, and if they differ it populates a updated_fields attribute.
So far so good but I have just realized that the comparison doesn't take into account the fields type.

Given that I pass everything in as strings from an object, the attributes are converted to the correct type but the extra fields are not.
So when I compare I might compare the same values but having different types, leading to errors.

In practise:

from typing import Optional, List, Dict, Union
from pydantic import BaseModel, root_validator

class MyModel(BaseModel):
    my_field_str: str = ""
    my_field_int: int = ""
    another_field_float: Optional[float] = 0.0
    
    updated_fields: List[str] = []
    extra: Dict[str, Union[str, int, float]] 
    
    class Config:
        """config class."""
        allow_population_by_field_name = True
        orm_mode = True
    
    
    @root_validator(pre=True)
    @classmethod
    def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]:  # type: ignore
        """Store extra fields passed to the model into an internal dict."""
        all_required_field_names = {
            field.alias for field in cls.__fields__.values() if field.alias != "extra"
        }  # to support alias

        extra: Dict[str, Any] = {}  # type: ignore
        for field_name in list(values):
            if field_name not in all_required_field_names:
                extra[field_name] = values.pop(field_name)
        values["extra"] = extra
        return values
    
    
    @staticmethod
    def _populate_updated_fields(values: dict[str, Any]) -> list[str]:  # type: ignore
        """Compute which fileds were updated."""
        updated_fields = []
        excluded_fields = ["extra", "updated_fields"]
        for k, v in values.items():
            if v is not None and k not in excluded_fields and v != values["extra"].get("orig_" + k, None):
                updated_fields.append(k) # PROBLEM: cast type of the field
        return updated_fields

    
    @root_validator()
    @classmethod
    def populate_updated_fields(cls, values: dict[str, Any]) -> dict[str, Any]:  # type: ignore
        values["updated_fields"] = cls._populate_updated_fields(values)
        return values
    
    
    
my_obj = MyModel.parse_obj({
    'my_field_str': 'hello',
    'my_field_int': '25',
    'another_field_float': '47.5',
    'orig_my_field_str': 'hello',
    'orig_my_field_int': '25',
    'orig_another_field_float': '47.5',
})

How can I perform the correct comparison taking into account the types? I thought about:

  1. Introspect the class and check what type is the field being compared to and convert the extra field on the fly (how?)
  2. Already convert the extra values when creating the object (not being model attributes I wouldn't know how to specify the type)
0 Answers
Related