Skip to content
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

Access to orm_mode object in root validator #821

Open
dmontagu opened this issue Sep 18, 2019 · 2 comments
Open

Access to orm_mode object in root validator #821

dmontagu opened this issue Sep 18, 2019 · 2 comments

Comments

@dmontagu
Copy link
Collaborator

@dmontagu dmontagu commented Sep 18, 2019

Feature Request

It would be nice if you could access the "source" object in a root_validator (coming soon; see #817) when the model was being initialized via a call to from_orm. This would enable almost any form of custom loading from an orm object possible by use of a root validator.

I think this can be accomplished now by making changes only to the GetterDict class:

class GetterDict:
    """
    Hack to make object's smell just enough like dicts for validate_model.
    """

    __slots__ = ('_obj', '_overrides')

    def __init__(self, obj: Any):
        self._obj = obj
        self._overrides: Optional[Dict] = None  # NEWLY ADDED

    # NEWLY ADDED
    def source_object(self):
        return self._obj

    # NEWLY ADDED
    def __setitem__(self, key, value):
        if self._overrides is None:
            self._overrides = {}
        self._overrides.__setitem__(key, value)

    def get(self, item: Any, default: Any) -> Any:
        # SLIGHTLY MODIFIED
        if self._overrides is None:
            # This check could be removed if we make the _overrides non-optional
            # but I think that would come with a *slightly* larger performance penalty if not used
            return getattr(self._obj, item, default)
        return self._overrides.get(item) or getattr(self._obj, item, default)

    def keys(self) -> Set[Any]:
        """
        We don't want to get any other attributes of obj if the model didn't explicitly ask for them
        """
        return set()

The only performance sacrifice if not making use of the feature is the check if self._overrides is None (it seems to me like the impact would be miniscule in practice).

This would allow things like:

from pydantic import BaseModel, Schema, root_validator
from pydantic.utils import GetterDict


class MyModelORM:
    def __init__(self, x):
        self.x = x

    @property
    def orm_property(self) -> str:
        return "hello"


class MyModel(BaseModel):
    x: int
    y: str = Schema(None)

    @root_validator(pre=True)
    def get_y(cls, values):
        if isinstance(values, GetterDict):
            # This is how you know it got called via from_orm
            values['y'] = values.source_object().orm_property
        return values

    class Config:
        orm_mode = True


print(MyModel.from_orm(MyModelORM(x=1)))
# MyModel x=1 y='hello'

@samuelcolvin I can't think of any breaking changes this would introduce, can you? Would this be interesting to you?

@dmontagu dmontagu mentioned this issue Sep 18, 2019
8 of 8 tasks complete
@dmontagu

This comment has been minimized.

Copy link
Collaborator Author

@dmontagu dmontagu commented Sep 18, 2019

Also, it might make sense to change the name of GetterDict to OrmGetterDict if this is the only way it is used. (And maybe to OrmDict if it now supports setting, as above.)

@samuelcolvin

This comment has been minimized.

Copy link
Owner

@samuelcolvin samuelcolvin commented Sep 20, 2019

Please review #822, I've tried to implement what you need a PR into the existing #817 PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.