You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have an Error class that optionally includes details, which should be a subclass of Pydantic's BaseModel. My aim is to specify the type of these details. Here's how I've implemented it:
With this, the details field is strongly typed and ensures that, if present, it's always an instance of Pydantic's BaseModel. However, when I call error.model_dump(), the details field serializes as an empty dictionary. Removing the bound constraint on ErrorDataT allows the model to serialize correctly, but at the expense of type validation, i.e., I can assign any value to error.details.
In the tests provided below, both should ideally pass. But currently, only one of them does based on the bound constraint:
With the constraint:test_can_not_use_non_base_models_as_error_details passes, buttest_sample_error_is_correctly_serialized fails.
Without the constraint:test_sample_error_is_correctly_serialized passes, buttest_can_not_use_non_base_models_as_error_details fails.
NOTE: This was working in v1 (using GenericModel). I apologize in advance if I didn't understand something while doing the migration from v1 to v2.
Example Code
frompydanticimportBaseModel, ValidationErrorfromtypingimportTypeVar, Generic, OptionalimportpytestErrorDataT=TypeVar("ErrorDataT", bound=BaseModel)
classErrorDetails(BaseModel):
foo: strclassErrorDetailsNonBaseModel:
other: strdef__init__(self, other) ->None:
self.other=otherclassError(BaseModel, Generic[ErrorDataT]):
message: strdetails: Optional[ErrorDataT]
sample_error=Error(
message="We just had an error",
details=ErrorDetails(foo="var"),
)
classTestApiErrorResponse:
deftest_sample_error_is_correctly_serialized(self):
assertsample_error.model_dump() == {
"message": "We just had an error",
"details": {
"foo": "var",
},
}
deftest_can_not_use_non_base_models_as_error_details(self):
withpytest.raises(ValidationError):
Error(
message="We just had an error",
details=ErrorDetailsNonBaseModel(other="some value"),
)
Thanks for your question! The current issue you're having is due to the fact that you haven't specified the generic parameter type when you instantiate the Error class.
If you do the following, your tests pass:
frompydanticimportBaseModel, ValidationErrorfromtypingimportTypeVar, Generic, OptionalimportpytestErrorDataT=TypeVar("ErrorDataT", bound=BaseModel)
classErrorDetails(BaseModel):
foo: strclassErrorDetailsNonBaseModel:
other: strdef__init__(self, other) ->None:
self.other=otherclassError(BaseModel, Generic[ErrorDataT]):
message: strdetails: Optional[ErrorDataT]
sample_error=Error[ErrorDetails](
message="We just had an error",
details=ErrorDetails(foo="var"),
)
The reason for this is that we default to serializing with the bound type of a TypeVar if the type being used for a generic isn't specified.
Another approach that you could use is this:
frompydanticimportBaseModel, SerializeAsAnyfromtypingimportTypeVar, Generic, Optional
ErrorDataT=TypeVar("ErrorDataT", bound=BaseModel)
classErrorDetails(BaseModel):
foo: str
classError(BaseModel, Generic[ErrorDataT]):
message: strdetails: Optional[SerializeAsAny[ErrorDataT]]
sample_error=Error(
message="We just had an error",
details=ErrorDetails(foo="var"),
)
Using the SerializeAsAny flag makes it such that whatever type ErrorDataT takes on, it's serialized as an Any type, but validated still against the ErrorDataT type schema.
All of this being said, we think that this behavior is confusing, so we've opened the PR mentioned above to fix this issue (so the original code you have should work, once that change is released).
Initial Checks
Description
I have an
Error
class that optionally includesdetails
, which should be a subclass of Pydantic's BaseModel. My aim is to specify the type of these details. Here's how I've implemented it:With this, the
details
field is strongly typed and ensures that, if present, it's always an instance of Pydantic's BaseModel. However, when I callerror.model_dump()
, thedetails
field serializes as an empty dictionary. Removing the bound constraint onErrorDataT
allows the model to serialize correctly, but at the expense of type validation, i.e., I can assign any value toerror.details
.In the tests provided below, both should ideally pass. But currently, only one of them does based on the bound constraint:
test_can_not_use_non_base_models_as_error_details
passes, buttest_sample_error_is_correctly_serialized
fails.test_sample_error_is_correctly_serialized
passes, buttest_can_not_use_non_base_models_as_error_details
fails.NOTE: This was working in v1 (using GenericModel). I apologize in advance if I didn't understand something while doing the migration from v1 to v2.
Example Code
Python, Pydantic & OS Version
The text was updated successfully, but these errors were encountered: