summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2008-03-23 16:36:47 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2008-03-23 16:36:47 +0000
commitdde64666607698c887775c6f3704e242a413dbac (patch)
treedc47d0b584bc80c30ed6f8728f87fcb214118a77
parent8ef1bded27632d818db2b24f733579d2f436e6bc (diff)
downloadsqlalchemy-dde64666607698c887775c6f3704e242a413dbac.tar.gz
- already-compiled mappers will still trigger compiles of
other uncompiled mappers when used [ticket:995]
-rw-r--r--CHANGES11
-rw-r--r--lib/sqlalchemy/orm/mapper.py15
-rw-r--r--test/ext/declarative.py24
-rw-r--r--test/orm/mapper.py18
4 files changed, 60 insertions, 8 deletions
diff --git a/CHANGES b/CHANGES
index ecd91ccc7..5481768a6 100644
--- 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.
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 3a6ea21cf..7e24c27c2 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -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()
diff --git a/test/ext/declarative.py b/test/ext/declarative.py
index 6b6b90ea0..5da2dded5 100644
--- a/test/ext/declarative.py
+++ b/test/ext/declarative.py
@@ -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):
diff --git a/test/orm/mapper.py b/test/orm/mapper.py
index 1228c6c77..aa27c4c81 100644
--- a/test/orm/mapper.py
+++ b/test/orm/mapper.py
@@ -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()