summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/loading.py48
-rw-r--r--lib/sqlalchemy/orm/mapper.py2
-rw-r--r--lib/sqlalchemy/orm/persistence.py4
-rw-r--r--lib/sqlalchemy/orm/query.py15
4 files changed, 38 insertions, 31 deletions
diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py
index 617f027d9..193980e6c 100644
--- a/lib/sqlalchemy/orm/loading.py
+++ b/lib/sqlalchemy/orm/loading.py
@@ -28,6 +28,7 @@ from .util import aliased
from .util import state_str
from .. import exc as sa_exc
from .. import util
+from ..engine import result_tuple
from ..sql import util as sql_util
@@ -56,7 +57,7 @@ def instances(query, cursor, context):
)
try:
- (process, labels) = list(
+ (process, labels, extra) = list(
zip(
*[
query_entity.row_processor(query, context, cursor)
@@ -66,7 +67,7 @@ def instances(query, cursor, context):
)
if not single_entity:
- keyed_tuple = util.lightweight_named_tuple("result", labels)
+ keyed_tuple = result_tuple(labels, extra)
while True:
context.partials = {}
@@ -138,7 +139,9 @@ def merge_result(querylib, query, iterator, load=True):
]
result = []
keys = [ent._label_name for ent in query._entities]
- keyed_tuple = util.lightweight_named_tuple("result", keys)
+ keyed_tuple = result_tuple(
+ keys, [ent.entities for ent in query._entities]
+ )
for row in iterator:
newrow = list(row)
for i in mapped_entities:
@@ -190,7 +193,6 @@ def load_on_ident(
query, key, refresh_state=None, with_for_update=None, only_load_props=None
):
"""Load the given identity key from the database."""
-
if key is not None:
ident = key[1]
identity_token = key[2]
@@ -452,10 +454,19 @@ def _instance_processor(
instance_state = attributes.instance_state
instance_dict = attributes.instance_dict
session_id = context.session.hash_key
- version_check = context.version_check
runid = context.runid
identity_token = context.identity_token
+ version_check = context.version_check
+ if version_check:
+ version_id_col = mapper.version_id_col
+ if version_id_col is not None:
+ if adapter:
+ version_id_col = adapter.columns[version_id_col]
+ version_id_getter = result._getter(version_id_col)
+ else:
+ version_id_getter = None
+
if not refresh_state and _polymorphic_from is not None:
key = ("loader", path.path)
if key in context.attributes and context.attributes[key].strategy == (
@@ -539,8 +550,10 @@ def _instance_processor(
currentload = not isnew
loaded_instance = False
- if version_check and not currentload:
- _validate_version_id(mapper, state, dict_, row, adapter)
+ if version_check and version_id_getter and not currentload:
+ _validate_version_id(
+ mapper, state, dict_, row, version_id_getter
+ )
else:
# create a new instance
@@ -667,7 +680,7 @@ def _instance_processor(
def ensure_no_pk(row):
identitykey = (
identity_class,
- tuple([row[column] for column in pk_cols]),
+ tuple_getter(row),
identity_token,
)
if not is_not_primary_key(identitykey[1]):
@@ -812,20 +825,11 @@ def _populate_partial(
return to_load
-def _validate_version_id(mapper, state, dict_, row, adapter):
+def _validate_version_id(mapper, state, dict_, row, getter):
- version_id_col = mapper.version_id_col
-
- if version_id_col is None:
- return
-
- if adapter:
- version_id_col = adapter.columns[version_id_col]
-
- if (
- mapper._get_state_attr_by_column(state, dict_, mapper.version_id_col)
- != row[version_id_col]
- ):
+ if mapper._get_state_attr_by_column(
+ state, dict_, mapper.version_id_col
+ ) != getter(row):
raise orm_exc.StaleDataError(
"Instance '%s' has version id '%s' which "
"does not match database-loaded version id '%s'."
@@ -834,7 +838,7 @@ def _validate_version_id(mapper, state, dict_, row, adapter):
mapper._get_state_attr_by_column(
state, dict_, mapper.version_id_col
),
- row[version_id_col],
+ getter(row),
)
)
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 82e68fd07..b84d41260 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2631,7 +2631,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
"""Return an identity-map key for use in storing/retrieving an
item from the identity map.
- :param row: A :class:`.RowProxy` instance. The columns which are
+ :param row: A :class:`.Row` instance. The columns which are
mapped by this :class:`.Mapper` should be locatable in the row,
preferably via the :class:`.Column` object directly (as is the case
when a :func:`.select` construct is executed), or via string names of
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index 31b8b0a20..95c5f8fa2 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -1522,7 +1522,7 @@ def _postfetch(
if returning_cols:
row = result.context.returned_defaults
if row is not None:
- for col in returning_cols:
+ for row_value, col in zip(row, returning_cols):
# pk cols returned from insert are handled
# distinctly, don't step on the values here
if col.primary_key and result.context.isinsert:
@@ -1534,7 +1534,7 @@ def _postfetch(
# when using declarative w/ single table inheritance
prop = mapper._columntoproperty.get(col)
if prop:
- dict_[prop.key] = row[col]
+ dict_[prop.key] = row_value
if refresh_flush:
load_evt_attrs.append(prop.key)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index f19ec5673..d237aa3bf 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -47,6 +47,7 @@ from .. import inspection
from .. import log
from .. import sql
from .. import util
+from ..engine import result_tuple
from ..sql import coercions
from ..sql import expression
from ..sql import roles
@@ -56,6 +57,7 @@ from ..sql.base import _generative
from ..sql.base import ColumnCollection
from ..sql.base import Generative
from ..sql.selectable import ForUpdateArg
+from ..util import collections_abc
__all__ = ["Query", "QueryContext", "aliased"]
@@ -3320,7 +3322,7 @@ class Query(Generative):
"""
try:
ret = self.one()
- if not isinstance(ret, tuple):
+ if not isinstance(ret, collections_abc.Sequence):
return ret
return ret[0]
except orm_exc.NoResultFound:
@@ -4259,7 +4261,7 @@ class _MapperEntity(_QueryEntity):
polymorphic_discriminator=self._polymorphic_discriminator,
)
- return _instance, self._label_name
+ return _instance, self._label_name, self.entities
def setup_context(self, query, context):
adapter = self._get_entity_clauses(query, context)
@@ -4414,7 +4416,7 @@ class Bundle(InspectionAttr):
:ref:`bundles` - includes an example of subclassing.
"""
- keyed_tuple = util.lightweight_named_tuple("result", labels)
+ keyed_tuple = result_tuple(labels, [() for l in labels])
def proc(row):
return keyed_tuple([proc(row) for proc in procs])
@@ -4517,7 +4519,7 @@ class _BundleEntity(_QueryEntity):
ent.setup_context(query, context)
def row_processor(self, query, context, result):
- procs, labels = zip(
+ procs, labels, extra = zip(
*[
ent.row_processor(query, context, result)
for ent in self._entities
@@ -4526,7 +4528,7 @@ class _BundleEntity(_QueryEntity):
proc = self.bundle.create_row_processor(query, procs, labels)
- return proc, self._label_name
+ return proc, self._label_name, ()
class _ColumnEntity(_QueryEntity):
@@ -4675,7 +4677,8 @@ class _ColumnEntity(_QueryEntity):
column = context.adapter.columns[column]
getter = result._getter(column)
- return getter, self._label_name
+
+ return getter, self._label_name, (self.expr, self.column)
def setup_context(self, query, context):
column = query._adapt_clause(self.column, False, True)