]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added new flag expire_on_flush=False to column_property(),
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Oct 2011 18:15:09 +0000 (14:15 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Oct 2011 18:15:09 +0000 (14:15 -0400)
    marks those properties that would otherwise be considered
    to be "readonly", i.e. derived from SQL expressions,
    to retain their value after a flush has occurred, including
    if the parent object itself was involved in an update.

CHANGES
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
test/orm/test_unitofwork.py

diff --git a/CHANGES b/CHANGES
index f34bd810ddb9a1c223633c43d91d9382ca13df3c..7d3dd04ec13e31ac63d87904932ff6188e045dd0 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -31,6 +31,12 @@ CHANGES
     attribute, provided the name is the same as that
     of the entity mapped column.
 
+  - Added new flag expire_on_flush=False to column_property(),
+    marks those properties that would otherwise be considered
+    to be "readonly", i.e. derived from SQL expressions,
+    to retain their value after a flush has occurred, including
+    if the parent object itself was involved in an update.
+
   - Enhanced the instrumentation in the ORM to support
     Py3K's new argument style of "required kw arguments",
     i.e. fn(a, b, *, c, d), fn(a, b, *args, c, d).
index b7f7884f24a3f01ee629bfe1971d7b81b1de966d..8c44682f2d5eaa75d5761e648f62a372e5007228 100644 (file)
@@ -686,6 +686,22 @@ def column_property(*args, **kwargs):
     :param doc:
           optional string that will be applied as the doc on the
           class-bound descriptor.
+    
+    :param expire_on_flush=True:
+        Disable expiry on flush.   A column_property() which refers
+        to a SQL expression (and not a single table-bound column)
+        is considered to be a "read only" property; populating it
+        has no effect on the state of data, and it can only return
+        database state.   For this reason a column_property()'s value
+        is expired whenever the parent object is involved in a 
+        flush, that is, has any kind of "dirty" state within a flush.
+        Setting this parameter to ``False`` will have the effect of
+        leaving any existing value present after the flush proceeds.
+        Note however that the :class:`.Session` with default expiration
+        settings still expires 
+        all attributes after a :meth:`.Session.commit` call, however.
+        New in 0.7.3.
+        
 
     :param extension:
         an
index b1a6b1a33f5e13aa360f9e8bd84a4b2d73b73de4..e6ec422b068750e390c694dea780c45f539c4296 100644 (file)
@@ -2250,7 +2250,8 @@ class Mapper(object):
 
             if mapper._readonly_props:
                 readonly = state.unmodified_intersection(
-                    [p.key for p in mapper._readonly_props]
+                    [p.key for p in mapper._readonly_props 
+                        if p.expire_on_flush or p.key not in state.dict]
                 )
                 if readonly:
                     state.expire_attributes(state.dict, readonly)
index b9ec20c86826ac3f6b7419dd8dd24bdcdb075064..aa01757ac07366a397585c65e256d343b086aec2 100644 (file)
@@ -55,6 +55,8 @@ class ColumnProperty(StrategizedProperty):
 
         :param descriptor:
 
+        :param expire_on_flush:
+
         :param extension:
 
         """
@@ -67,6 +69,7 @@ class ColumnProperty(StrategizedProperty):
         self.descriptor = kwargs.pop('descriptor', None)
         self.extension = kwargs.pop('extension', None)
         self.active_history = kwargs.pop('active_history', False)
+        self.expire_on_flush = kwargs.pop('expire_on_flush', True)
 
         if 'doc' in kwargs:
             self.doc = kwargs.pop('doc')
index c81f15c9836b565890035c8b27fdadcce1bb8fc1..be781109d29b35c93b7ca7909b4acec52ece85bf 100644 (file)
@@ -940,14 +940,23 @@ class ColumnPropertyTest(fixtures.MappedTest):
         mapper(Data, data, properties={
             'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b)
         })
-        self._test()
+        self._test(True)
+
+    def test_no_refresh(self):
+        Data, data = self.classes.Data, self.tables.data
+
+        mapper(Data, data, properties={
+            'aplusb':column_property(data.c.a + literal_column("' '") + data.c.b, 
+                        expire_on_flush=False)
+        })
+        self._test(False)
 
     def test_refreshes_post_init(self):
         Data, data = self.classes.Data, self.tables.data
 
         m = mapper(Data, data)
         m.add_property('aplusb', column_property(data.c.a + literal_column("' '") + data.c.b))
-        self._test()
+        self._test(True)
 
     def test_with_inheritance(self):
         subdata, data, Data = (self.tables.subdata,
@@ -967,7 +976,7 @@ class ColumnPropertyTest(fixtures.MappedTest):
         sess.flush()
         eq_(sd1.aplusb, "hello there")
 
-    def _test(self):
+    def _test(self, expect_expiry):
         Data = self.classes.Data
 
         sess = create_session()
@@ -980,7 +989,11 @@ class ColumnPropertyTest(fixtures.MappedTest):
 
         d1.b = "bye"
         sess.flush()
-        eq_(d1.aplusb, "hello bye")
+        if expect_expiry:
+            eq_(d1.aplusb, "hello bye")
+        else:
+            eq_(d1.aplusb, "hello there")
+
 
         d1.b = 'foobar'
         d1.aplusb = 'im setting this explicitly'