]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- already-compiled mappers will still trigger compiles of
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Mar 2008 16:36:47 +0000 (16:36 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Mar 2008 16:36:47 +0000 (16:36 +0000)
 other uncompiled mappers when used [ticket:995]

CHANGES
lib/sqlalchemy/orm/mapper.py
test/ext/declarative.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index ecd91ccc708a625cef07a34f550ee378988b8a86..5481768a6e483fe444de740beb5a6e8298039923 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,6 +5,9 @@ CHANGES
 0.4.5
 =====
 - orm
+    - added a more aggressive check for "uncompiled mappers", 
+      helps particularly with declarative layer [ticket:995]
+      
     - Added comparable_property(), adds query Comparator behavior
       to regular, unmanaged Python properties
 
@@ -79,8 +82,6 @@ CHANGES
       stickier behavior than before which is why it's
       off by default.
 
-
-
 - extensions
     - The "synonym" function is now directly usable with
       "declarative".  Pass in the decorated property using the
@@ -94,7 +95,11 @@ CHANGES
     - Declarative also gained @synonym_for(...) and
       @comparable_using(...), front-ends for synonym and
       comparable_property.
-
+    
+    - Improvements to mapper compilation when using declarative;
+      already-compiled mappers will still trigger compiles of
+      other uncompiled mappers when used [ticket:995]
+      
     - Declarative will complete setup for Columns lacking names,
       allows a more DRY syntax.
 
index 3a6ea21cf6dc2a8b9262d8c9e6d25fa6e050719d..7e24c27c2f517e7b5583ce2df82b0c1a143b8d12 100644 (file)
@@ -23,6 +23,7 @@ from sqlalchemy.orm.interfaces import MapperProperty, EXT_CONTINUE, PropComparat
 __all__ = ['Mapper', 'class_mapper', 'object_mapper', '_mapper_registry']
 
 _mapper_registry = weakref.WeakKeyDictionary()
+__new_mappers = False
 
 # a list of MapperExtensions that will be installed in all mappers by default
 global_extensions = []
@@ -93,7 +94,7 @@ class Mapper(object):
                 # we make, theres workarounds but it starts to get really crazy (its crazy enough
                 # the SQL that gets generated) so just require an alias
                 raise exceptions.ArgumentError("Mapping against a Select object requires that it has a name.  Use an alias to give it a name, i.e. s = select(...).alias('myselect')")
-
+            
         self.class_ = class_
         self.entity_name = entity_name
         self.primary_key_argument = primary_key
@@ -164,7 +165,8 @@ class Mapper(object):
         self._compile_properties()
         self._compile_pks()
         self._compile_selectable()
-
+        global __new_mappers
+        __new_mappers = True
         self.__log("constructed")
 
     def __log(self, msg):
@@ -224,13 +226,15 @@ class Mapper(object):
     def compile(self):
         """Compile this mapper into its final internal format.
         """
-
-        if self.__props_init:
+        
+        global __new_mappers
+        if self.__props_init and not __new_mappers:
             return self
         _COMPILE_MUTEX.acquire()
         try:
+
             # double-check inside mutex
-            if self.__props_init:
+            if self.__props_init and not __new_mappers:
                 return self
 
             # initialize properties on all mappers
@@ -238,6 +242,7 @@ class Mapper(object):
                 if not mapper.__props_init:
                     mapper.__initialize_properties()
 
+            __new_mappers = False
             return self
         finally:
             _COMPILE_MUTEX.release()
index 6b6b90ea058aabe20e4c22fc5ebd3866066a4888..5da2dded5ee18ff0928999b9a03adb64c5ee4ff6 100644 (file)
@@ -56,6 +56,30 @@ class DeclarativeTest(TestBase, AssertsExecutionResults):
         a1 = sess.query(Address).filter(Address.email=='two').one()
         self.assertEquals(a1, Address(email='two'))
         self.assertEquals(a1.user, User(name='u1'))
+    
+    def test_recompile_on_othermapper(self):
+        """declarative version of the same test in mappers.py"""
+
+        from sqlalchemy.orm import mapperlib
+
+        class User(Base):
+            __tablename__ = 'users'
+
+            id = Column('id', Integer, primary_key=True)
+            name = Column('name', String(50))
+
+        class Address(Base):
+            __tablename__ = 'addresses'
+
+            id = Column('id', Integer, primary_key=True)
+            email = Column('email', String(50))
+            user_id = Column('user_id', Integer, ForeignKey('users.id'))
+            user = relation("User", primaryjoin=user_id==User.id, backref="addresses")
+
+        assert mapperlib._Mapper__new_mappers is True
+        u = User()
+        assert User.addresses
+        assert mapperlib._Mapper__new_mappers is False
 
     def test_nice_dependency_error(self):
         class User(Base):
index 1228c6c77917333e88cabf88ec9913434e212056..aa27c4c8151779c5167a62f8abf5af1c010d8457 100644 (file)
@@ -73,7 +73,25 @@ class MapperTest(MapperSuperTest):
             assert False
         except exceptions.ArgumentError, e:
             assert "could not assemble any primary key columns for mapped table 'foo'" in str(e)
+    
+    def test_recompile_on_othermapper(self):
+        """test the global '__new_mappers' flag such that a compile 
+        trigger on an already-compiled mapper still triggers a check against all mappers."""
 
+        from sqlalchemy.orm import mapperlib
+        
+        mapper(User, users)
+        compile_mappers()
+        assert mapperlib._Mapper__new_mappers is False
+        
+        m = mapper(Address, addresses, properties={'user':relation(User, backref="addresses")})
+        
+        assert m._Mapper__props_init is False
+        assert mapperlib._Mapper__new_mappers is True
+        u = User()
+        assert User.addresses
+        assert mapperlib._Mapper__new_mappers is False
+        
     def test_compileonsession(self):
         m = mapper(User, users)
         session = create_session()