-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Serialization change from v1 to v2 for a custom type that subclasses stdlib dataclass. #11740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Your In your case, Pydantic does something similar and assumes However, this behavior is arguably not correct, depending on the context. This was also reported in python/cpython#119260. I think it is reasonable to exclude dataclass subclasses (which themselves aren't proper dataclasses) from this check, but I'll need to check with the team how disruptive the change is considering this is a breaking change, and as such we might have to wait for V3. |
Thanks for the reply!
I tried the following in a brazen attempt to fool pydantic v2, a serialization that returns the instance, but pydantic v2 sees through my plan and converts it to a @classmethod
def __get_pydantic_core_schema__(cls, _source, _handler):
"""Override pydantic validation for our custom type"""
return core_schema.no_info_plain_validator_function(
cls.validate,
serialization=core_schema.plain_serializer_function_ser_schema(
lambda o: o
)
) So is there no way to say, do-not-serialize-this-instance, i.e., pretty-please with sugar on top, even if you think you can serialize it, don't and just return the object? If not, this is too major a breaking change from v1 for me to upgrade. Throughout much of my REST view code I have this kind of pattern: async def post(self, mod: MyModel):
"""Create an instance ..."""
data = mod.dict()
instance = await call_into_my_business_logic(**data)
... With the above, in v1, I pass an instance of my custom type into my business logic while with v2 I end up passing in a dict. Code is exploding everywhere. I'm happy to monkey patch if there's a known solution with current 2.x. |
This is due to an unfortunate behavior, as even when using a serializer schema, Pydantic also applies the logic I described with Actually, I think we can consider what I described in my previous comment as a bug. |
So, in pure python it would be: def is_dataclass(obj):
"""Returns True if obj is a dataclass or an instance of a
dataclass, but not a subclass or instance of a subclass.
"""
# get the class, either obj itself, or its type
cls = obj if isinstance(obj, type) else type(obj)
# check the class' __dict__ for the sentinel attribute; can't use hasattr because it would check the mro
return '__dataclass_fields__' in cls.__dict__ But we need this in rust, in the pydantic-core package? Is there any way to monkey patch with pure python? I'd like some way to move forward with my upgrade while waiting for an official patch. |
Not the prettiest, but you can define the following property as a workaround: class Custom(Base):
...
@property
def __dataclass_fields__(self):
raise AttributeError |
No, not pretty, but I have to say, kinda genius! 😉 |
Closing in favor of #11773, with a simpler repro. We'll keep track of this issue for V3. |
Initial Checks
Description
I have an application with a custom type,
Custom
, that subclasses from a 3rd party class, which in turn is a stdlibdataclass
. In v1, if I have aBaseModel
that usesCustom
as a field type and serialize an instance of the model, the value will be the instance ofCustom
. In v2, pydantic "sees" it is adataclass
and serializes it into adict
. This is breaking my application and I have yet to figure out how to turn off this undesired serialization.Full example below, but briefly:
In V2, even though I have provided no serialization for
Custom
, pydantic sees that it is adataclass
and is autogenerating the serialization. IfCustom
was a regular class (i.e., inherited implicitly fromobject
) it would return an instance, but subclassing adataclass
-decorated class, it serializes it to adict
. This is unexpected and breaking my app everywhere I'm expecting an instance, but finding a dict.In my fully working demo illustrating the problem, you'll see in the output that:
My question: How do I get v1 behavior when serializing such a model in v2?
Example Code
Python, Pydantic & OS Version
The text was updated successfully, but these errors were encountered: