from sqlalchemy import Column from sqlalchemy import exc as sa_exc from sqlalchemy import ForeignKey from sqlalchemy import Integer from sqlalchemy import MetaData from sqlalchemy import String from sqlalchemy import Table from sqlalchemy import Unicode from sqlalchemy.orm import backref from sqlalchemy.orm import clear_mappers from sqlalchemy.orm import configure_mappers from sqlalchemy.orm import relationship from sqlalchemy.orm import Session from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import fixtures class CompileTest(fixtures.MappedTest): """test various mapper compilation scenarios""" def teardown_test(self): clear_mappers() def test_with_polymorphic(self): metadata = MetaData() 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: pass class Employee: pass class Product: pass class OrderProduct: pass order_join = order.select().alias("pjoin") self.mapper_registry.map_imperatively( Order, order, with_polymorphic=("*", order_join), polymorphic_on=order_join.c.type, polymorphic_identity="order", properties={ "orderproducts": relationship( OrderProduct, lazy="select", backref="order" ) }, ) self.mapper_registry.map_imperatively( Product, product, properties={ "orderproducts": relationship( OrderProduct, lazy="select", backref="product" ) }, ) self.mapper_registry.map_imperatively( Employee, employee, properties={ "orders": relationship( Order, lazy="select", backref="employee" ) }, ) self.mapper_registry.map_imperatively(OrderProduct, orderproduct) # this requires that the compilation of order_mapper's "surrogate # mapper" occur after the initial setup of MapperProperty objects on # the mapper. configure_mappers() def test_conflicting_backref_one(self): """test that conflicting backrefs raises an exception""" metadata = MetaData() 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: pass class Product: pass class OrderProduct: pass order_join = order.select().alias("pjoin") self.mapper_registry.map_imperatively( Order, order, with_polymorphic=("*", order_join), polymorphic_on=order_join.c.type, polymorphic_identity="order", properties={ "orderproducts": relationship( OrderProduct, lazy="select", backref="product" ) }, ) self.mapper_registry.map_imperatively( Product, product, properties={ "orderproducts": relationship( OrderProduct, lazy="select", backref="product" ) }, ) self.mapper_registry.map_imperatively(OrderProduct, orderproduct) assert_raises_message( sa_exc.ArgumentError, "Error creating backref", configure_mappers ) def test_misc_one(self, connection, metadata): node_table = Table( "node", metadata, Column("node_id", Integer, primary_key=True), Column("name_index", Integer, nullable=True), ) node_name_table = Table( "node_name", metadata, Column("node_name_id", Integer, primary_key=True), Column("node_id", Integer, ForeignKey("node.node_id")), Column("host_id", Integer, ForeignKey("host.host_id")), Column("name", String(64), nullable=False), ) host_table = Table( "host", metadata, Column("host_id", Integer, primary_key=True), Column("hostname", String(64), nullable=False, unique=True), ) metadata.create_all(connection) connection.execute(node_table.insert(), dict(node_id=1, node_index=5)) class Node: pass class NodeName: pass class Host: pass self.mapper_registry.map_imperatively(Node, node_table) self.mapper_registry.map_imperatively(Host, host_table) self.mapper_registry.map_imperatively( NodeName, node_name_table, properties={ "node": relationship(Node, backref=backref("names")), "host": relationship(Host), }, ) sess = Session(connection) assert sess.get(Node, 1).names == [] def test_conflicting_backref_two(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: pass class B: pass self.mapper_registry.map_imperatively( A, a, properties={"b": relationship(B, backref="a")} ) self.mapper_registry.map_imperatively( B, b, properties={"a": relationship(A, backref="b")} ) assert_raises_message( sa_exc.ArgumentError, "Error creating backref", configure_mappers ) def test_conflicting_backref_subclass(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: pass class B: pass class C(B): pass self.mapper_registry.map_imperatively( A, a, properties={ "b": relationship(B, backref="a"), "c": relationship(C, backref="a"), }, ) self.mapper_registry.map_imperatively(B, b) self.mapper_registry.map_imperatively(C, None, inherits=B) assert_raises_message( sa_exc.ArgumentError, "Error creating backref", configure_mappers )