1. aodag
  2. sqlalchemy-namedfilter

Commits

Michael Bayer  committed 9ed3f12

added check for conflicting backrefs + unit test
identified unit test where mapper properties must be set up before the surrogate mapper is created

Comments (0)

Files changed (5)

File CHANGES Modified

View file
  • Ignore whitespace
  • Hide word diff
 the given object was formerly attached to was garbage collected;
 otherwise still requires you explicitly remove the instance from 
 the previous Session.
+- fixes to mapper compilation, checking for more error conditions
 
 0.2.3
 - overhaul to mapper compilation to be deferred.  this allows mappers

File lib/sqlalchemy/orm/mapper.py Modified

View file
  • Ignore whitespace
  • Hide word diff
         constructed in an arbitrary order, completing their relationships when they have all been established."""
         if self.__is_compiled:
             return self
-        #print "COMPILING!", self.class_key
+        #print "COMPILING!", self.class_key, "non primary: ", self.non_primary
         self.__is_compiled = True
         self._compile_extensions()
         self._compile_inheritance()
         self._compile_tables()
+        self._compile_properties()
         self._compile_selectable()
-        self._compile_properties()
         self._initialize_properties()
         
         # compile some other mappers which have backrefs to this mapper

File lib/sqlalchemy/orm/properties.py Modified

View file
  • Ignore whitespace
  • Hide word diff
         """template method for subclasses of PropertyLoader"""
         pass
     
+    def _get_target_class(self):
+        """return the target class of the relation, even if the property has not been initialized yet."""
+        if isinstance(self.argument, type):
+            return self.argument
+        else:
+            return self.argument.class_
+        
     def do_init(self, key, parent):
         import sqlalchemy.orm
         if isinstance(self.argument, type):
             mapper._compile_property(self.key, relation);
         else:
             # else set one of us as the "backreference"
+            parent = prop.parent.primary_mapper()
+            if parent.class_ is not mapper.props[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.props[self.key].mapper.class_)))
             if not mapper.props[self.key].is_backref:
                 prop.is_backref=True
                 prop._dependency_processor.is_backref=True

File test/orm/alltests.py Modified

View file
  • Ignore whitespace
  • Hide word diff
         'orm.poly_linked_list',
 
 	'orm.entity',
+	'orm.compile',
 	'orm.manytomany',
 	'orm.onetoone',
 	'orm.inheritance',

File test/orm/compile.py Added

View file
  • Ignore whitespace
  • Hide word diff
+from sqlalchemy import *
+import testbase
+
+class CompileTest(testbase.AssertMixin):
+    """test various mapper compilation scenarios"""
+    def tearDownAll(self):
+        clear_mappers()
+        
+    def testone(self):
+        global metadata, order, employee, product, tax, orderproduct
+        metadata = BoundMetaData(engine)
+
+        order = Table('orders', metadata, 
+            Column('id', Integer, primary_key=True),
+            Column('employee_id', Integer, ForeignKey('employees.id'), nullable=False),
+            Column('type', Unicode(16)))
+
+        employee = Table('employees', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('name', Unicode(16), unique=True, nullable=False))
+
+        product = Table('products', metadata,
+            Column('id', Integer, primary_key=True),
+        )
+
+        orderproduct = Table('orderproducts', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('order_id', Integer, ForeignKey("orders.id"), nullable=False),
+            Column('product_id', Integer, ForeignKey("products.id"), nullable=False),
+        )
+
+        class Order(object):
+            pass
+
+        class Employee(object):
+            pass
+
+        class Product(object):
+            pass
+
+        class OrderProduct(object):
+            pass
+
+        order_join = order.select().alias('pjoin')
+
+        order_mapper = mapper(Order, order, 
+            select_table=order_join, 
+            polymorphic_on=order_join.c.type, 
+            polymorphic_identity='order',
+            properties={
+                'orderproducts': relation(OrderProduct, lazy=True, backref='order')}
+            )
+
+        mapper(Product, product,
+            properties={
+                'orderproducts': relation(OrderProduct, lazy=True, backref='product')}
+            )
+
+        mapper(Employee, employee,
+            properties={
+                'orders': relation(Order, lazy=True, backref='employee')})
+
+        mapper(OrderProduct, orderproduct)
+        
+        # this requires that the compilation of order_mapper's "surrogate mapper" occur after
+        # the initial setup of MapperProperty objects on the mapper.
+        class_mapper(Product).compile()
+
+    def testtwo(self):
+        """test that conflicting backrefs raises an exception"""
+        global metadata, order, employee, product, tax, orderproduct
+        metadata = BoundMetaData(engine)
+
+        order = Table('orders', metadata, 
+            Column('id', Integer, primary_key=True),
+            Column('type', Unicode(16)))
+
+        product = Table('products', metadata,
+            Column('id', Integer, primary_key=True),
+        )
+
+        orderproduct = Table('orderproducts', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('order_id', Integer, ForeignKey("orders.id"), nullable=False),
+            Column('product_id', Integer, ForeignKey("products.id"), nullable=False),
+        )
+
+        class Order(object):
+            pass
+
+        class Product(object):
+            pass
+
+        class OrderProduct(object):
+            pass
+
+        order_join = order.select().alias('pjoin')
+
+        order_mapper = mapper(Order, order, 
+            select_table=order_join, 
+            polymorphic_on=order_join.c.type, 
+            polymorphic_identity='order',
+            properties={
+                'orderproducts': relation(OrderProduct, lazy=True, backref='product')}
+            )
+
+        mapper(Product, product,
+            properties={
+                'orderproducts': relation(OrderProduct, lazy=True, backref='product')}
+            )
+
+        mapper(OrderProduct, orderproduct)
+
+        try:
+            class_mapper(Product).compile()
+            assert False
+        except exceptions.ArgumentError, e:
+            assert str(e).index("Backrefs do not match") > -1
+
+if __name__ == '__main__':
+    testbase.main()