--- /dev/null
+from sqlalchemy import *
+import sqlalchemy.attributes as attributes
+
+engine = create_engine('sqlite://', echo=True)
+
+class Tree(object):
+ def __init__(self, name='', father=None):
+ self.name = name
+ self.father = father
+ def __str__(self):
+ return '<TreeNode: %s>' % self.name
+ def __repr__(self):
+ return self.__str__()
+
+table = Table('tree', engine,
+ Column('id', Integer, primary_key=True),
+ Column('name', String(64), nullable=False),
+ Column('father_id', Integer, ForeignKey('tree.id'), nullable=True),)
+
+assign_mapper(Tree, table,
+ properties={
+ # set up a backref using a string
+ #'father':relation(Tree, foreignkey=table.c.id,primaryjoin=table.c.father_id==table.c.id, backref='childs')},
+
+ # or set up using the backref() function, which allows arguments to be passed
+ 'childs':relation(Tree, foreignkey=table.c.father_id, primaryjoin=table.c.father_id==table.c.id, backref=backref('father', uselist=False, foreignkey=table.c.id))},
+ )
+
+table.create()
+root = Tree('root')
+child1 = Tree('child1', root)
+child2 = Tree('child2', root)
+child3 = Tree('child3', child1)
+
+objectstore.commit()
+
+print root.childs
+print child1.childs
+print child2.childs
+print child2.father
+print child3.father
from properties import *
import mapper as mapperlib
-__all__ = ['relation', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
+__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
'mapper', 'clear_mappers', 'objectstore', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension',
'assign_mapper', 'cascade_mappers'
]
else:
return EagerLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs)
+def backref(name, **kwargs):
+ return BackRef(name, **kwargs)
+
def deferred(*columns, **kwargs):
+ """returns a DeferredColumnProperty, which indicates this object attributes should only be loaded
+ from its corresponding table column when first accessed."""
return DeferredColumnProperty(*columns, **kwargs)
def mapper(class_, table=None, *args, **params):
print "'use_alias' argument to relation() is deprecated. eager loads automatically alias-ize tables now."
self.order_by = order_by
self.attributeext=attributeext
- self.backref = backref
+ if isinstance(backref, str):
+ self.backref = BackRef(backref)
+ else:
+ self.backref = backref
self.is_backref = is_backref
def copy(self):
# if a backref name is defined, set up an extension to populate
# attributes in the other direction
if self.backref is not None:
- self.attributeext = attributes.GenericBackrefExtension(self.backref)
+ self.attributeext = self.backref.get_extension()
# set our class attribute
self._set_class_attribute(parent.class_, key)
if self.backref is not None:
- # try to set a LazyLoader on our mapper referencing the parent mapper
- if not self.mapper.props.has_key(self.backref):
- if self.secondaryjoin is not None:
- # if setting up a backref to a many-to-many, reverse the order
- # of the "primary" and "secondary" joins
- pj = self.secondaryjoin
- sj = self.primaryjoin
- else:
- pj = self.primaryjoin
- sj = None
- self.mapper.add_property(self.backref, LazyLoader(self.parent, self.secondary, pj, sj, backref=self.key, is_backref=True));
- else:
- # else set one of us as the "backreference"
- if not self.mapper.props[self.backref].is_backref:
- self.is_backref=True
+ self.backref.compile(self)
elif not objectstore.global_attributes.is_class_managed(parent.class_, key):
raise ArgumentError("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__))
kwargs = util.constructor_args(oldprop)
mapper.set_property(key, class_(**kwargs ))
-
+class BackRef(object):
+ """stores the name of a backreference property as well as a relation (PropertyLoader),
+ used to construct more customized backrefs"""
+ def __init__(self, key, **kwargs):
+ self.key = key
+ self.kwargs = kwargs
+ def compile(self, prop):
+ # try to set a LazyLoader on our mapper referencing the parent mapper
+ if not prop.mapper.props.has_key(self.key):
+ if prop.secondaryjoin is not None:
+ # if setting up a backref to a many-to-many, reverse the order
+ # of the "primary" and "secondary" joins
+ pj = prop.secondaryjoin
+ sj = prop.primaryjoin
+ else:
+ pj = prop.primaryjoin
+ sj = None
+ relation = LazyLoader(prop.parent, prop.secondary, pj, sj, backref=prop.key, is_backref=True, **self.kwargs)
+ prop.mapper.add_property(self.key, relation);
+ else:
+ # else set one of us as the "backreference"
+ if not prop.mapper.props[self.key].is_backref:
+ prop.is_backref=True
+
+ def get_extension(self):
+ return attributes.GenericBackrefExtension(self.key)
+
class EagerLazyOption(GenericOption):
"""an option that switches a PropertyLoader to be an EagerLoader or LazyLoader"""
def __init__(self, key, toeager = True, **kwargs):