What does the update
argument to model_validate
do, and am I using it correctly?
#748
-
First Check
Commit to Help
Example Codefrom sqlmodel import SQLModel
class A(SQLModel):
foo: str
class B(A):
bar: str
a = A(foo="a")
print(a) # > foo='a'
# A cannot be validated as B, because it is missing a field
# b = B.model_validate(a)
# This throws a RecursionError
b = B.model_validate(a, update={"bar": "b"})
print(b) DescriptionHi, I'm new to SQLModel. From reading the tutorial I gather that However I'm confused about how to use this in practice. I noticed that the method takes an optional @classmethod
def model_validate(
cls: Type[_TSQLModel],
obj: Any,
*,
strict: Union[bool, None] = None,
from_attributes: Union[bool, None] = None,
context: Union[Dict[str, Any], None] = None,
update: Union[Dict[str, Any], None] = None,
) -> _TSQLModel:
return sqlmodel_validate(
cls=cls,
obj=obj,
strict=strict,
from_attributes=from_attributes,
context=context,
update=update,
) This is undocumented, but in #523 somebody mentioned that
I was hoping this meant I could call pass a dictionary to However when I run the code above, I get an unexpected RecursionError:
Operating SystemmacOS Operating System DetailsNo response SQLModel Version0.0.14 Python VersionPython 3.11.5 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
@MatMoore I just ran into this as well. From digging into things, it looks when you pass a model along with the def sqlmodel_validate(
cls: Type[_TSQLModel],
obj: Any,
*,
strict: Union[bool, None] = None,
from_attributes: Union[bool, None] = None,
context: Union[Dict[str, Any], None] = None,
update: Union[Dict[str, Any], None] = None,
) -> _TSQLModel:
if not is_table_model_class(cls):
new_obj: _TSQLModel = cls.__new__(cls)
else:
# If table, create the new instance normally to make SQLAlchemy create
# the _sa_instance_state attribute
# The wrapper of this function should use with _partial_init()
with partial_init():
new_obj = cls()
# SQLModel Override to get class SQLAlchemy __dict__ attributes and
# set them back in after creating the object
old_dict = new_obj.__dict__.copy()
use_obj = obj
if isinstance(obj, dict) and update:
use_obj = {**obj, **update}
elif update:
use_obj = ObjectWithUpdateWrapper(obj=obj, update=update)
cls.__pydantic_validator__.validate_python(
use_obj,
strict=strict,
from_attributes=from_attributes,
context=context,
self_instance=new_obj,
) Unfortunately, it seems that there is a bug, because as the validator is running, it tries to check for an attribute called @dataclass
class ObjectWithUpdateWrapper:
obj: Any
update: Dict[str, Any]
def __getattribute__(self, __name: str) -> Any: # NOTE: Recursion occurs when `__name` is "update".
if __name in self.update:
return self.update[__name]
return getattr(self.obj, __name) That being said, it seems like a workaround might be to to dump the model as a dict and use that instead (since it would not cause the from sqlmodel import SQLModel
class A(SQLModel):
foo: str
class B(A):
bar: str
a = A(foo="a")
print(a) # > foo='a'
# A cannot be validated as B, because it is missing a field
# b = B.model_validate(a)
# Possible workaround?
b = B.model_validate(a.model_dump(), update={"bar": "b"})
print(b) |
Beta Was this translation helpful? Give feedback.
@MatMoore I just ran into this as well. From digging into things, it looks when you pass a model along with the
update
param, it creates aObjectWithUpdateWrapper
which is what actually gets validated: