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
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
- 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.
__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 = []
# 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
self._compile_properties()
self._compile_pks()
self._compile_selectable()
-
+ global __new_mappers
+ __new_mappers = True
self.__log("constructed")
def __log(self, msg):
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
if not mapper.__props_init:
mapper.__initialize_properties()
+ __new_mappers = False
return self
finally:
_COMPILE_MUTEX.release()
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):
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()