summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/mapping/properties.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/mapping/properties.py')
-rw-r--r--lib/sqlalchemy/mapping/properties.py110
1 files changed, 72 insertions, 38 deletions
diff --git a/lib/sqlalchemy/mapping/properties.py b/lib/sqlalchemy/mapping/properties.py
index 72cb44749..61bb3da36 100644
--- a/lib/sqlalchemy/mapping/properties.py
+++ b/lib/sqlalchemy/mapping/properties.py
@@ -28,8 +28,6 @@ class ColumnProperty(MapperProperty):
setattr(object, self.key, value)
def get_history(self, obj, passive=False):
return objectstore.global_attributes.get_history(obj, self.key, passive=passive)
- def hash_key(self):
- return "ColumnProperty(%s)" % repr([hash_key(c) for c in self.columns])
def _copy(self):
return ColumnProperty(*self.columns)
@@ -60,8 +58,6 @@ class DeferredColumnProperty(ColumnProperty):
self.group = kwargs.get('group', None)
ColumnProperty.__init__(self, *columns)
- def hash_key(self):
- return "DeferredColumnProperty(%s)" % repr([hash_key(c) for c in self.columns])
def _copy(self):
return DeferredColumnProperty(*self.columns)
@@ -125,7 +121,7 @@ class PropertyLoader(MapperProperty):
"""describes an object property that holds a single item or list of items that correspond
to a related database table."""
- def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, live=False, association=None, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False):
+ def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, live=False, association=None, use_alias=False, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False):
self.uselist = uselist
self.argument = argument
self.secondary = secondary
@@ -135,19 +131,24 @@ class PropertyLoader(MapperProperty):
self.private = private
self.live = live
self.association = association
- self.selectalias = selectalias
+ if isinstance(selectalias, str):
+ print "'selectalias' argument to property is deprecated. please use 'use_alias=True'"
+ self.use_alias = True
+ else:
+ self.use_alias = use_alias
self.order_by = order_by
self.attributeext=attributeext
self.backref = backref
self.is_backref = is_backref
- self._hash_key = "%s(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.__class__.__name__, hash_key(self.argument), hash_key(secondary), hash_key(primaryjoin), hash_key(secondaryjoin), hash_key(foreignkey), repr(uselist), repr(private), hash_key(self.order_by))
def _copy(self):
- return self.__class__(self.mapper, self.secondary, self.primaryjoin, self.secondaryjoin, self.foreignkey, self.uselist, self.private)
+ x = self.__class__.__new__(self.__class__)
+ x.__dict__.update(self.__dict__)
+ return x
+
+ def init_subclass(self, key, parent):
+ pass
- def hash_key(self):
- return self._hash_key
-
def init(self, key, parent):
import sqlalchemy.mapping
if isinstance(self.argument, type):
@@ -212,6 +213,8 @@ class PropertyLoader(MapperProperty):
elif not objectstore.global_attributes.is_class_managed(parent.class_, key):
raise "Non-primary property created for attribute '%s' on class '%s', but that attribute is not managed! Insure that the primary mapper for this class defines this property" % (key, parent.class_.__name__)
+ self.init_subclass(key, parent)
+
def _is_primary(self):
"""a return value of True indicates we are the primary PropertyLoader for this loader's
attribute on our mapper's class. It means we can set the object's attribute behavior
@@ -354,6 +357,8 @@ class PropertyLoader(MapperProperty):
pass
def delete_obj(self, *args, **kwargs):
pass
+ def _primary_mapper(self):
+ return self
def register_dependencies(self, uowcommit):
"""tells a UOWTransaction what mappers are dependent on which, with regards
@@ -569,8 +574,7 @@ class PropertyLoader(MapperProperty):
objectstore.global_attributes.create_history(instance, self.key, self.uselist)
class LazyLoader(PropertyLoader):
- def init(self, key, parent):
- PropertyLoader.init(self, key, parent)
+ def init_subclass(self, key, parent):
(self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.table, self.primaryjoin, self.secondaryjoin, self.foreignkey)
# determine if our "lazywhere" clause is the same as the mapper's
# get() clause. then we can just use mapper.get()
@@ -658,10 +662,15 @@ def create_lazy_clause(table, primaryjoin, secondaryjoin, foreignkey):
class EagerLoader(PropertyLoader):
"""loads related objects inline with a parent query."""
- def init(self, key, parent):
- PropertyLoader.init(self, key, parent)
-
+ def init_subclass(self, key, parent, recursion_stack=None):
parent._has_eager = True
+
+ if recursion_stack is None:
+ recursion_stack = {}
+
+ if self.use_alias:
+ pass
+
# figure out tables in the various join clauses we have, because user-defined
# whereclauses that reference the same tables will be converted to use
# aliases of those tables
@@ -670,7 +679,6 @@ class EagerLoader(PropertyLoader):
if self.secondaryjoin is not None:
[self.to_alias.append(f) for f in self.secondaryjoin._get_from_objects()]
try:
-# del self.to_alias[parent.primarytable]
del self.to_alias[parent.table]
except KeyError:
pass
@@ -680,8 +688,8 @@ class EagerLoader(PropertyLoader):
# or primary join condition to reference the aliased table (and the order_by).
# else we set up the target clause objects as what they are defined in the
# superclass.
- if self.selectalias is not None:
- self.eagertarget = self.target.alias(self.selectalias)
+ if self.use_alias:
+ self.eagertarget = self.target.alias()
aliasizer = Aliasizer(self.target, aliases={self.target:self.eagertarget})
if self.secondaryjoin is not None:
self.eagersecondary = self.secondaryjoin.copy_container()
@@ -700,6 +708,36 @@ class EagerLoader(PropertyLoader):
self.eager_order_by[i].accept_visitor(aliasizer)
else:
self.eager_order_by = self.order_by
+
+ # we have to propigate the "use_alias" fact into
+ # any sub-mappers that are also eagerloading so that they create a unique tablename
+ # as well. this copies our child mapper and replaces any eager properties on the
+ # new mapper with an equivalent eager property, just containing use_alias=True
+ eagerprops = []
+ for key, prop in self.mapper.props.iteritems():
+ if isinstance(prop, EagerLoader) and not prop.use_alias:
+ eagerprops.append(prop)
+ if len(eagerprops):
+ recursion_stack[self] = True
+ self.mapper = self.mapper.copy()
+ try:
+ for prop in eagerprops:
+ p = prop._copy()
+ p.use_alias=True
+
+ self.mapper.props[prop.key] = p
+
+ if recursion_stack.has_key(prop):
+ raise "Circular eager load relationship detected on " + str(self.mapper) + " " + key + repr(self.mapper.props)
+
+ p.init_subclass(prop.key, prop.parent, recursion_stack)
+
+ p.eagerprimary = p.eagerprimary.copy_container()
+ aliasizer = Aliasizer(p.parent.table, aliases={p.parent.table:self.eagertarget})
+ p.eagerprimary.accept_visitor(aliasizer)
+ finally:
+ del recursion_stack[self]
+
else:
self.eagertarget = self.target
self.eagerprimary = self.primaryjoin
@@ -726,19 +764,12 @@ class EagerLoader(PropertyLoader):
else:
towrap = self.parent.table
- if eagertable is not None:
- eagerprimary = self.eagerprimary.copy_container()
- aliasizer = Aliasizer(self.parent.table, aliases={self.parent.table:eagertable})
- eagerprimary.accept_visitor(aliasizer)
- else:
- eagerprimary = self.eagerprimary
-
if self.secondaryjoin is not None:
- statement._outerjoin = sql.outerjoin(towrap, self.secondary, eagerprimary).outerjoin(self.eagertarget, self.eagersecondary)
+ statement._outerjoin = sql.outerjoin(towrap, self.secondary, self.eagerprimary).outerjoin(self.eagertarget, self.eagersecondary)
if self.order_by is False and self.secondary.default_order_by() is not None:
statement.order_by(*self.secondary.default_order_by())
else:
- statement._outerjoin = towrap.outerjoin(self.eagertarget, eagerprimary)
+ statement._outerjoin = towrap.outerjoin(self.eagertarget, self.eagerprimary)
if self.order_by is False and self.eagertarget.default_order_by() is not None:
statement.order_by(*self.eagertarget.default_order_by())
@@ -746,7 +777,6 @@ class EagerLoader(PropertyLoader):
statement.order_by(*util.to_list(self.eager_order_by))
statement.append_from(statement._outerjoin)
- #statement.append_column(self.eagertarget)
recursion_stack[self] = True
try:
for key, value in self.mapper.props.iteritems():
@@ -778,11 +808,11 @@ class EagerLoader(PropertyLoader):
def _instance(self, row, imap, result_list=None):
"""gets an instance from a row, via this EagerLoader's mapper."""
- # if we have an alias for our mapper's table via the selectalias
+ # if we have an alias for our mapper's table via the use_alias
# parameter, we need to translate the
# aliased columns from the incoming row into a new row that maps
# the values against the columns of the mapper's original non-aliased table.
- if self.selectalias is not None:
+ if self.use_alias:
fakerow = {}
fakerow = util.DictDecorator(row)
for c in self.eagertarget.c:
@@ -802,9 +832,8 @@ class GenericOption(MapperOption):
tokens = key.split('.', 1)
if len(tokens) > 1:
oldprop = mapper.props[tokens[0]]
- kwargs = util.constructor_args(oldprop)
- kwargs['argument'] = self.process_by_key(oldprop.mapper.copy(), tokens[1])
- newprop = oldprop.__class__(**kwargs)
+ newprop = oldprop._copy()
+ newprop.argument = self.process_by_key(oldprop.mapper.copy(), tokens[1])
mapper.set_property(tokens[0], newprop)
else:
self.create_prop(mapper, tokens[0])
@@ -832,10 +861,15 @@ class EagerLazyOption(GenericOption):
else:
class_ = LazyLoader
- # create a clone of the class using mostly the arguments from the original
- submapper = mapper.props[key].mapper
- kwargs = util.constructor_args(mapper.props[key], **self.kwargs)
- mapper.set_property(key, class_(**kwargs ))
+ oldprop = mapper.props[key]
+ newprop = class_.__new__(class_)
+ newprop.__dict__.update(oldprop.__dict__)
+ newprop.init_subclass(key, mapper)
+ if self.kwargs.get('selectalias', None):
+ newprop.use_alias = True
+ elif self.kwargs.get('use_alias', None) is not None:
+ newprop.use_alias = self.kwargs['use_alias']
+ mapper.set_property(key, newprop)
class DeferredOption(GenericOption):
def __init__(self, key, defer=False, **kwargs):