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
Serializing @property #935
Comments
I think the only way you could serialize properties now would be to override the serialization-related methods to manually inject the property values. I don't think that should be hard, but I'm not sure. It's probably worth documenting if it is straightforward. In general, it would be tough to do this "automatically" because properties are "stored" very differently than the model data. And for many people it would be a breaking change. I think it could make sense to add support for this via config, but only if it didn't add more checks to the serialization process (which I think it might have to). |
I wonder if implementing something like the schema/field as a decorator would be a good approach? class Rectangle(BaseModel):
width: bool
length: int
@property
@field(title='Area')
def area(self) -> int:
return self.width * self.length And on init, the internal fields dict could grab attributes that have the decorator and act accordingly. I'd say the property above would set to Could possibly used on methods to support I'd be happy to get a PR going with some guidance if this feature is desired by others (esp the property serializtion, not so much the method) |
Happy to review a PR, hard to know how clean/performant it would be until someone tries it. |
I think this is a really good idea, and like your proposed api @bradodarb, but adding as few checks as possible for non-users of the feature would be critical because of how the serialization functions are called recursively on each item in lists/dicts. |
I was also looking for the serialization part of the |
We do this in some of our models by overriding the dict method and injecting the properties to the resulting dict. I think the @field decorator is a really good idea to solve this in the framework in a stable way. |
We do this in some of our models by overriding the dict method and injecting the properties to the resulting dict. I think the @field decorator is a really good idea to solve this in the framework in a stable way. Example on how this can be done by overriding dict() @classmethod
def get_properties(cls):
return [prop for prop in cls.__dict__ if isinstance(cls.__dict__[prop], property)]
def dict(
self,
*,
include: Union['AbstractSetIntStr', 'DictIntStrAny'] = None,
exclude: Union['AbstractSetIntStr', 'DictIntStrAny'] = None,
by_alias: bool = False,
skip_defaults: bool = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
) -> Dict[str, Any]:
"""Override the dict function to include our properties"""
attribs = super().dict(
include=include,
exclude=exclude,
by_alias=by_alias,
skip_defaults=skip_defaults,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults
)
props = self.get_properties()
# Include and exclude properties
if include:
props = [prop for prop in props if prop in include]
if exclude:
props = [prop for prop in props if prop not in exclude]
# Update the attribute dict with the properties
if props:
attribs.update({prop: getattr(self, prop) for prop in props})
return attribs |
Interesting feature, I was looking for it. Is there a PR for review/testing? Thank you |
Not yet. I think we still need to decide on exactly how it would work. Questions:
|
What if instead of decorators we use a default value? A bit similar to the field factory from dataclass. Ex: class Rectangle(BaseModel):
width: int
length: int
area: int = property_value('area')
@property
def area(self) -> int:
return self.width * self.length We could even add a default validator for those properties that block setting the attribute value |
I think that's uglier than the decorator approach outlined above. |
Regarding the cache question. I'd go for no caching and use cached_property (from 3.8) if needed be. People in 3.7 would need to handle it manually, but it'd be pretty straightforward in future python versions. |
Regarding mypy, the easiest way to deal with it would probably be to leverage the One possible approach -- I have no idea if the following would work, but something like: # actual return type may or may not be the `property` type
def pydantic_property(*args, ** kwargs) -> Type[property]
... Hopefully this would mean mypy would treat Another version to consider would be: F = TypeVar("F")
def pydantic_property(...) -> Callable[[F], F]:
...
@pydantic_property(...)
@property
def blah(...)
... This would allow the use of whichever property decorator you wanted ( I personally think it would be best to support both cached and un-cached versions, and it would be great if there was a way to leverage existing decorators so we didn't have to maintain that behavior ourselves. If we want to move forward with this, I'm happy to try to look into the above more carefully. |
@samuelcolvin, @PrettyWood will V2 offer a solution to this issue ? |
I did not check the interaction with fast api, thanks for the heads up |
Wrote a little helper package, which also works for serialization. Maybe this package helps some of you. |
How to deal with properties in mapped entity models when using ORM models? |
See pydantic/pydantic#935 for a reference
See pydantic/pydantic#935 for a reference
See pydantic/pydantic#935 for a reference
See pydantic/pydantic#935 for a reference
See pydantic/pydantic#935 for a reference
For some reason, it works in our code base with sqlalchemy ORM models. I just discovered this issue because we couldn't get it to work in a |
This will be fixed via #5502 feedback their (or via new issues if the PR has been merged) very welcome. |
Update: |
Hi @dmontagu and @samuelcolvin , is this feature going to be backported to v1? I am asking because v2 is still in alpha and this is quite important feature. Is it difficult to backport it or write the similar functionality on v1? Or maybe for v1 you will recommend to use some third-party library like in this comment: #935 (comment)? |
It won't be backported, v1 is receiving security and bug fixes only now. |
In the meantime, here's a shim which you might be able to get working. I can't claim it's particularly efficient, and it only outputs dynamic properties into the output of functions like
|
…k of pydantic support Property fset is actually not supported in pydantic<v2 (at least not without some work) See: pydantic/pydantic#935 Signed-off-by: Braden Mars <bradenmars@bradenmars.me>
Question
I've pored over the documentation and issue and can't seem to find an example for serializing members other than class attributes.
I'd also think that properties without setters would default to readOnly on the schema side as well.
It did seem there was a way to get something close to what I'm after via ORM mode and masking a property on the ORM model, but I'd hate to need to create a class just to contain properties.
Going over BaseModel._calculate_keys it seems BaseModel will not yield what I'm after.
Still, I'm hoping that it's just been a long day and I overlooked something :)
The text was updated successfully, but these errors were encountered: