backref collision detection

Issue #832 resolved
Mike Bayer repo owner created an issue
from sqlalchemy import *
from sqlalchemy.orm import *

engine = create_engine('sqlite://')
meta = MetaData(engine)

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
class C(object):pass

mapper(A, a, properties={
    'b':relation(B, backref='a')
})
mapper(B, b, properties={
    'a':relation(A, backref='b')
})

compile_mappers()

should raise an exception. properties.py should ideally have this patch:

--- lib/sqlalchemy/orm/properties.py    (revision 3649)
+++ lib/sqlalchemy/orm/properties.py    (working copy)
@@ -152,7 +152,7 @@
         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:
@@ -676,7 +676,7 @@

         # try to set a LazyLoader on our mapper referencing the parent mapper
         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
@@ -684,29 +684,18 @@
             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=prop.key, 
+                                      is_backref=True,
                                       **self.kwargs)
             mapper._compile_property(self.key, relation);
+            prop.reverse_property = mapper.get_property(self.key)
+            mapper.get_property(self.key).reverse_property = prop
         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)))
         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("Property '%s' already exists on mapper '%s'" % (self.key, mapper))

     def get_extension(self):
         """Return an attribute extension to use with this backreference."""

some tests are failing though (the proper backref config isn't fully set up).

Comments (2)

  1. Log in to comment