summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2022-09-19 23:30:23 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2022-09-19 23:30:23 +0000
commitaab6005dda456c8647192cd62064c67e6a5558ff (patch)
tree7b8aea21b09f269cf18f250e367e281cb9a4f4b6
parent16359c41217b43b27f1b3540fc58687734784c15 (diff)
parent9020e104ebc7f4a0a3a44395c558fb0e119c32ee (diff)
downloadsqlalchemy-aab6005dda456c8647192cd62064c67e6a5558ff.tar.gz
Merge "add raiseload to load_only()" into main
-rw-r--r--doc/build/changelog/unreleased_20/load_only_raiseload.rst7
-rw-r--r--lib/sqlalchemy/orm/strategy_options.py39
-rw-r--r--test/orm/test_deferred.py13
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()