summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-01-10 22:32:51 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-01-10 22:32:51 +0000
commitf1cb136a626fa4fcac8ab2dc926c8ecc00a16636 (patch)
treefee23c5ea9df9225b80f5591aeae37f3fe6a5b04
parent062b8c0eb1ce22944503c28e2de6d2cabe78acf7 (diff)
downloadsqlalchemy-f1cb136a626fa4fcac8ab2dc926c8ecc00a16636.tar.gz
- added a mapper() flag "eager_defaults"; when set to
True, defaults that are generated during an INSERT or UPDATE operation are post-fetched immediately, instead of being deferred until later. This mimics the old 0.3 behavior.
-rw-r--r--CHANGES6
-rw-r--r--lib/sqlalchemy/orm/mapper.py31
-rw-r--r--test/sql/defaults.py8
3 files changed, 35 insertions, 10 deletions
diff --git a/CHANGES b/CHANGES
index c0a63a546..329709909 100644
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,12 @@ CHANGES
- proper error message is raised when trying to
access expired instance attributes with no session
present
+
+ - added a mapper() flag "eager_defaults"; when set to
+ True, defaults that are generated during an INSERT
+ or UPDATE operation are post-fetched immediately,
+ instead of being deferred until later. This mimics
+ the old 0.3 behavior.
- dialects
- finally added PGMacAddr type to postgres
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index dcfae524f..1451f4336 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -72,7 +72,8 @@ class Mapper(object):
batch=True,
column_prefix=None,
include_properties=None,
- exclude_properties=None):
+ exclude_properties=None,
+ eager_defaults=False):
"""Construct a new mapper.
Mappers are normally constructed via the [sqlalchemy.orm#mapper()]
@@ -111,6 +112,7 @@ class Mapper(object):
self.allow_null_pks = allow_null_pks
self.delete_orphans = []
self.batch = batch
+ self.eager_defaults = eager_defaults
self.column_prefix = column_prefix
self.polymorphic_on = polymorphic_on
self._eager_loaders = util.Set()
@@ -1086,7 +1088,7 @@ class Mapper(object):
for rec in update:
(state, params, mapper, connection, value_params) = rec
c = connection.execute(statement.values(value_params), params)
- mapper._postfetch(connection, table, state, c, c.last_updated_params(), value_params)
+ mapper._postfetch(uowtransaction, connection, table, state, c, c.last_updated_params(), value_params)
# testlib.pragma exempt:__hash__
updated_objects.add((state, connection))
@@ -1110,7 +1112,7 @@ class Mapper(object):
for i, col in enumerate(mapper._pks_by_table[table]):
if mapper._get_state_attr_by_column(state, col) is None and len(primary_key) > i:
mapper._set_state_attr_by_column(state, col, primary_key[i])
- mapper._postfetch(connection, table, state, c, c.last_inserted_params(), value_params)
+ mapper._postfetch(uowtransaction, connection, table, state, c, c.last_inserted_params(), value_params)
# synchronize newly inserted ids from one table to the next
# TODO: this fires off more than needed, try to organize syncrules
@@ -1133,7 +1135,7 @@ class Mapper(object):
if 'after_update' in mapper.extension.methods:
mapper.extension.after_update(mapper, connection, state.obj())
- def _postfetch(self, connection, table, state, resultproxy, params, value_params):
+ def _postfetch(self, uowtransaction, connection, table, state, resultproxy, params, value_params):
"""After an ``INSERT`` or ``UPDATE``, assemble newly generated
values on an instance. For columns which are marked as being generated
on the database side, set up a group-based "deferred" loader
@@ -1151,7 +1153,13 @@ class Mapper(object):
self._set_state_attr_by_column(state, c, params[c.key])
if deferred_props:
- _expire_state(state, deferred_props)
+ # TODO: need a unit test for this functionality
+ if self.eager_defaults:
+ _instance_key = self._identity_key_from_state(state)
+ state.dict['_instance_key'] = _instance_key
+ uowtransaction.session.query(self)._get(_instance_key, refresh_instance=state, only_load_props=deferred_props)
+ else:
+ _expire_state(state, deferred_props)
def _delete_obj(self, states, uowtransaction):
"""Issue ``DELETE`` statements for a list of objects.
@@ -1310,7 +1318,14 @@ class Mapper(object):
if not currentload and context.version_check and self.version_id_col and self._get_attr_by_column(instance, self.version_id_col) != row[self.version_id_col]:
raise exceptions.ConcurrentModificationError("Instance '%s' version of %s does not match %s" % (instance, self._get_attr_by_column(instance, self.version_id_col), row[self.version_id_col]))
-
+ elif refresh_instance:
+ # out of band refresh_instance detected (i.e. its not in the session.identity_map)
+ # honor it anyway. this can happen if a _get() occurs within save_obj(), such as
+ # when eager_defaults is True.
+ state = refresh_instance
+ instance = state.obj()
+ isnew = state.runid != context.runid
+ currentload = True
else:
if self.__should_log_debug:
self.__log_debug("_instance(): identity key %s not in session" % str(identitykey))
@@ -1526,12 +1541,12 @@ def has_mapper(object):
object_session = None
def _load_scalar_attributes(instance, attribute_names):
+ mapper = object_mapper(instance)
+
global object_session
if not object_session:
from sqlalchemy.orm.session import object_session
-
session = object_session(instance)
- mapper = object_mapper(instance)
if not session:
try:
session = mapper.get_session()
diff --git a/test/sql/defaults.py b/test/sql/defaults.py
index 98b379995..a41ef4a17 100644
--- a/test/sql/defaults.py
+++ b/test/sql/defaults.py
@@ -407,8 +407,12 @@ class SequenceTest(PersistTest):
def testseqnonpk(self):
"""test sequences fire off as defaults on non-pk columns"""
- sometable.insert().execute(name="somename")
- sometable.insert().execute(name="someother")
+ result = sometable.insert().execute(name="somename")
+ assert 'id' in result.postfetch_cols()
+
+ result = sometable.insert().execute(name="someother")
+ assert 'id' in result.postfetch_cols()
+
sometable.insert().execute(
{'name':'name3'},
{'name':'name4'}