diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2022-09-19 23:30:23 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2022-09-19 23:30:23 +0000 |
| commit | aab6005dda456c8647192cd62064c67e6a5558ff (patch) | |
| tree | 7b8aea21b09f269cf18f250e367e281cb9a4f4b6 | |
| parent | 16359c41217b43b27f1b3540fc58687734784c15 (diff) | |
| parent | 9020e104ebc7f4a0a3a44395c558fb0e119c32ee (diff) | |
| download | sqlalchemy-aab6005dda456c8647192cd62064c67e6a5558ff.tar.gz | |
Merge "add raiseload to load_only()" into main
| -rw-r--r-- | doc/build/changelog/unreleased_20/load_only_raiseload.rst | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategy_options.py | 39 | ||||
| -rw-r--r-- | test/orm/test_deferred.py | 13 |
3 files changed, 50 insertions, 9 deletions
diff --git a/doc/build/changelog/unreleased_20/load_only_raiseload.rst b/doc/build/changelog/unreleased_20/load_only_raiseload.rst new file mode 100644 index 000000000..6d5716f4f --- /dev/null +++ b/doc/build/changelog/unreleased_20/load_only_raiseload.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: usecase, orm + + Added :paramref:`_orm.load_only.raiseload` parameter to the + :func:`_orm.load_only` loader option, so that the unloaded attributes may + have "raise" behavior rather than lazy loading. Previously there wasn't + really a way to do this with the :func:`_orm.load_only` option directly. diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index 2aed60b61..2748556fd 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -165,9 +165,9 @@ class _AbstractLoad(traversals.GenerativeOnTraversal, LoaderOption): return cloned def load_only( - self: Self_AbstractLoad, *attrs: _AttrType + self: Self_AbstractLoad, *attrs: _AttrType, raiseload: bool = False ) -> Self_AbstractLoad: - """Indicate that for a particular entity, only the given list + r"""Indicate that for a particular entity, only the given list of column-based attribute names should be loaded; all others will be deferred. @@ -200,14 +200,27 @@ class _AbstractLoad(traversals.GenerativeOnTraversal, LoaderOption): if the column property is defined with ``deferred=True`` for the :func:`.column_property` function. + :param \*attrs: Attributes to be loaded, all others will be deferred. + + :param raiseload: raise :class:`.InvalidRequestError` rather than + lazy loading a value when a deferred attribute is accessed. Used + to prevent unwanted SQL from being emitted. + + .. versionadded:: 2.0 + """ cloned = self._set_column_strategy( attrs, {"deferred": False, "instrument": True}, ) + + wildcard_strategy = {"deferred": True, "instrument": True} + if raiseload: + wildcard_strategy["raiseload"] = True + cloned = cloned._set_column_strategy( ("*",), - {"deferred": True, "instrument": True}, + wildcard_strategy, ) return cloned @@ -612,9 +625,9 @@ class _AbstractLoad(traversals.GenerativeOnTraversal, LoaderOption): :param key: Attribute to be deferred. - :param raiseload: raise :class:`.InvalidRequestError` if the column - value is to be loaded from emitting SQL. Used to prevent unwanted - SQL from being emitted. + :param raiseload: raise :class:`.InvalidRequestError` rather than + lazy loading a value when the deferred attribute is accessed. Used + to prevent unwanted SQL from being emitted. .. versionadded:: 1.4 @@ -2397,11 +2410,11 @@ def contains_eager(*keys: _AttrType, **kw: Any) -> _AbstractLoad: @loader_unbound_fn -def load_only(*attrs: _AttrType) -> _AbstractLoad: +def load_only(*attrs: _AttrType, raiseload: bool = False) -> _AbstractLoad: # TODO: attrs against different classes. we likely have to # add some extra state to Load of some kind _, lead_element, _ = _parse_attr_argument(attrs[0]) - return Load(lead_element).load_only(*attrs) + return Load(lead_element).load_only(*attrs, raiseload=raiseload) @loader_unbound_fn @@ -2453,7 +2466,9 @@ def defaultload(*keys: _AttrType) -> _AbstractLoad: @loader_unbound_fn -def defer(key: _AttrType, *addl_attrs: _AttrType, **kw: Any) -> _AbstractLoad: +def defer( + key: _AttrType, *addl_attrs: _AttrType, raiseload: bool = False +) -> _AbstractLoad: if addl_attrs: util.warn_deprecated( "The *addl_attrs on orm.defer is deprecated. Please use " @@ -2461,6 +2476,12 @@ def defer(key: _AttrType, *addl_attrs: _AttrType, **kw: Any) -> _AbstractLoad: "indicate a path.", version="1.3", ) + + if raiseload: + kw = {"raiseload": raiseload} + else: + kw = {} + return _generate_from_keys(Load.defer, (key,) + addl_attrs, False, kw) diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py index 277ebfd88..e20ce6324 100644 --- a/test/orm/test_deferred.py +++ b/test/orm/test_deferred.py @@ -2317,6 +2317,19 @@ class RaiseLoadTest(fixtures.DeclarativeMappedTest): "x", ) + def test_load_only_raise_option_raise_column_plain(self): + A = self.classes.A + s = fixture_session() + + a1 = s.query(A).options(load_only(A.y, A.z, raiseload=True)).first() + assert_raises_message( + sa.exc.InvalidRequestError, + "'A.x' is not available due to raiseload=True", + getattr, + a1, + "x", + ) + def test_deferred_raise_option_load_column_unexpire(self): A = self.classes.A s = fixture_session() |
