self.comparator = PropertyLoader.Comparator(self)
self.join_depth = join_depth
self.strategy_class = strategy_class
-
+
if cascade is not None:
self.cascade = mapperutil.CascadeOptions(cascade)
else:
PropertyLoader.logger = logging.class_logger(PropertyLoader)
class BackRef(object):
- """Stores the name of a backreference property as well as options
- to be used on the resulting PropertyLoader.
- """
+ """Attached to a PropertyLoader to indicate a complementary reverse relationship.
+
+ Can optionally create the complementing PropertyLoader if one does not exist already."""
- def __init__(self, key, **kwargs):
+ def __init__(self, key, _prop=None, **kwargs):
self.key = key
self.kwargs = kwargs
+ self.prop = _prop
def compile(self, prop):
- """Called by the owning PropertyLoader to set up a
- backreference on the PropertyLoader's mapper.
- """
-
- # try to set a LazyLoader on our mapper referencing the parent mapper
+ if self.prop:
+ return
+
+ self.prop = prop
+
mapper = prop.mapper.primary_mapper()
- if not mapper.get_property(self.key, raiseerr=False) is not None:
+ if mapper.get_property(self.key, raiseerr=False) is None:
pj = self.kwargs.pop('primaryjoin', None)
sj = self.kwargs.pop('secondaryjoin', None)
- # the backref property is set on the primary mapper
+
parent = prop.parent.primary_mapper()
self.kwargs.setdefault('viewonly', prop.viewonly)
self.kwargs.setdefault('post_update', prop.post_update)
+
relation = PropertyLoader(parent, prop.secondary, pj, sj,
- backref=prop.key, is_backref=True,
+ backref=BackRef(prop.key, _prop=prop),
+ is_backref=True,
**self.kwargs)
+
mapper._compile_property(self.key, relation);
- elif not isinstance(mapper.get_property(self.key), PropertyLoader):
- raise exceptions.ArgumentError(
- "Can't create backref '%s' on mapper '%s'; an incompatible "
- "property of that name already exists" % (self.key, str(mapper)))
+
+ prop.reverse_property = mapper.get_property(self.key)
+ mapper.get_property(self.key).reverse_property = prop
+
else:
- # else set one of us as the "backreference"
- parent = prop.parent.primary_mapper()
- if parent.class_ is not mapper.get_property(self.key)._get_target_class():
- raise exceptions.ArgumentError(
- "Backrefs do not match: backref '%s' expects to connect to %s, "
- "but found a backref already connected to %s" %
- (self.key, str(parent.class_), str(mapper.get_property(self.key).mapper.class_)))
- if not mapper.get_property(self.key).is_backref:
- prop.is_backref=True
- if not prop.viewonly:
- prop._dependency_processor.is_backref=True
- # reverse_property used by dependencies.ManyToManyDP to check
- # association table operations
- prop.reverse_property = mapper.get_property(self.key)
- mapper.get_property(self.key).reverse_property = prop
+ raise exceptions.ArgumentError("Error creating backref '%s' on relation '%s': property of that name exists on mapper '%s'" % (self.key, prop, mapper))
def get_extension(self):
"""Return an attribute extension to use with this backreference."""
))
mapper(Design, design, properties=dict(
- parts=relation(Part, private=True, backref="design"),
inheritedParts=relation(InheritedPart, private=True, backref="design"),
))
))
class_mapper(Design).add_property("type", relation(DesignType, lazy=False, backref="designs"))
- class_mapper(Part).add_property("design", relation(Design, lazy=False, backref="parts"))
+
+ class_mapper(Part).add_property("design", relation(Design, lazy=False, backref=backref("parts", cascade="all, delete-orphan")))
+
#Part.mapper.add_property("designType", relation(DesignType))
d = Design()
class CompileTest(AssertMixin):
"""test various mapper compilation scenarios"""
- def tearDownAll(self):
+ def tearDown(self):
clear_mappers()
def testone(self):
class_mapper(Product).compile()
assert False
except exceptions.ArgumentError, e:
- assert str(e).index("Backrefs do not match") > -1
+ assert str(e).index("Error creating backref ") > -1
def testthree(self):
metadata = MetaData(testbase.db)
finally:
metadata.drop_all()
+ def testfour(self):
+ meta = MetaData()
+
+ a = Table('a', meta, Column('id', Integer, primary_key=True))
+ b = Table('b', meta, Column('id', Integer, primary_key=True), Column('a_id', Integer, ForeignKey('a.id')))
+
+ class A(object):pass
+ class B(object):pass
+
+ mapper(A, a, properties={
+ 'b':relation(B, backref='a')
+ })
+ mapper(B, b, properties={
+ 'a':relation(A, backref='b')
+ })
+
+ try:
+ compile_mappers()
+ assert False
+ except exceptions.ArgumentError, e:
+ assert str(e).index("Error creating backref") > -1
+
if __name__ == '__main__':
testbase.main()
location_name_mapper = mapper(LocationName, location_name_table)
location_mapper = mapper(Location, location_table, properties = {
- 'issue': relation(Issue, backref='locations'),
+ 'issue': relation(Issue, backref=backref('locations', lazy=False, cascade="all, delete-orphan")),
'_name': relation(LocationName),
})
- issue_mapper.add_property('locations', relation(Location, lazy=False, private=True, backref='issue'))
-
page_size_mapper = mapper(PageSize, page_size_table)
magazine_mapper = mapper(Magazine, magazine_table, properties = {