Files
sqlalchemy/test/ext/test_automap.py
T
Mike Bayer 5508388f03 - The :mod:sqlalchemy.ext.automap extension will now set
``cascade="all, delete-orphan"`` automatically on a one-to-many
relationship/backref where the foreign key is detected as containing
one or more non-nullable columns.  This argument is present in the
keywords passed to :func:`.automap.generate_relationship` in this
case and can still be overridden.  Additionally, if the
:class:`.ForeignKeyConstraint` specifies ``ondelete="CASCADE"``
for a non-nullable or ``ondelete="SET NULL"`` for a nullable set
of columns, the argument ``passive_deletes=True`` is also added to the
relationship.  Note that not all backends support reflection of
ondelete, but backends that do include Postgresql and MySQL.
fixes #3210
2014-09-22 23:00:45 -04:00

306 lines
9.1 KiB
Python

from sqlalchemy.testing import fixtures
from ..orm._fixtures import FixtureTest
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import relationship, interfaces, configure_mappers
from sqlalchemy.ext.automap import generate_relationship
from sqlalchemy.testing.mock import Mock
from sqlalchemy import String, Integer, ForeignKey
from sqlalchemy import testing
from sqlalchemy.testing.schema import Table, Column
class AutomapTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
FixtureTest.define_tables(metadata)
def test_relationship_o2m_default(self):
Base = automap_base(metadata=self.metadata)
Base.prepare()
User = Base.classes.users
Address = Base.classes.addresses
a1 = Address(email_address='e1')
u1 = User(name='u1', addresses_collection=[a1])
assert a1.users is u1
def test_relationship_explicit_override_o2m(self):
Base = automap_base(metadata=self.metadata)
prop = relationship("addresses", collection_class=set)
class User(Base):
__tablename__ = 'users'
addresses_collection = prop
Base.prepare()
assert User.addresses_collection.property is prop
Address = Base.classes.addresses
a1 = Address(email_address='e1')
u1 = User(name='u1', addresses_collection=set([a1]))
assert a1.user is u1
def test_relationship_explicit_override_m2o(self):
Base = automap_base(metadata=self.metadata)
prop = relationship("users")
class Address(Base):
__tablename__ = 'addresses'
users = prop
Base.prepare()
User = Base.classes.users
assert Address.users.property is prop
a1 = Address(email_address='e1')
u1 = User(name='u1', address_collection=[a1])
assert a1.users is u1
def test_relationship_self_referential(self):
Base = automap_base(metadata=self.metadata)
Base.prepare()
Node = Base.classes.nodes
n1 = Node()
n2 = Node()
n1.nodes_collection.append(n2)
assert n2.nodes is n1
def test_naming_schemes(self):
Base = automap_base(metadata=self.metadata)
def classname_for_table(base, tablename, table):
return str("cls_" + tablename)
def name_for_scalar_relationship(
base, local_cls, referred_cls, constraint):
return "scalar_" + referred_cls.__name__
def name_for_collection_relationship(
base, local_cls, referred_cls, constraint):
return "coll_" + referred_cls.__name__
Base.prepare(
classname_for_table=classname_for_table,
name_for_scalar_relationship=name_for_scalar_relationship,
name_for_collection_relationship=name_for_collection_relationship
)
User = Base.classes.cls_users
Address = Base.classes.cls_addresses
u1 = User()
a1 = Address()
u1.coll_cls_addresses.append(a1)
assert a1.scalar_cls_users is u1
def test_relationship_m2m(self):
Base = automap_base(metadata=self.metadata)
Base.prepare()
Order, Item = Base.classes.orders, Base.classes['items']
o1 = Order()
i1 = Item()
o1.items_collection.append(i1)
assert o1 in i1.orders_collection
def test_relationship_explicit_override_forwards_m2m(self):
Base = automap_base(metadata=self.metadata)
class Order(Base):
__tablename__ = 'orders'
items_collection = relationship(
"items",
secondary="order_items",
collection_class=set)
Base.prepare()
Item = Base.classes['items']
o1 = Order()
i1 = Item()
o1.items_collection.add(i1)
# it is 'order_collection' because the class name is
# "Order" !
assert isinstance(i1.order_collection, list)
assert o1 in i1.order_collection
def test_relationship_pass_params(self):
Base = automap_base(metadata=self.metadata)
mock = Mock()
def _gen_relationship(
base, direction, return_fn, attrname,
local_cls, referred_cls, **kw):
mock(base, direction, attrname)
return generate_relationship(
base, direction, return_fn,
attrname, local_cls, referred_cls, **kw)
Base.prepare(generate_relationship=_gen_relationship)
assert set(tuple(c[1]) for c in mock.mock_calls).issuperset([
(Base, interfaces.MANYTOONE, "nodes"),
(Base, interfaces.MANYTOMANY, "keywords_collection"),
(Base, interfaces.MANYTOMANY, "items_collection"),
(Base, interfaces.MANYTOONE, "users"),
(Base, interfaces.ONETOMANY, "addresses_collection"),
])
class CascadeTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
Table(
"a", metadata,
Column('id', Integer, primary_key=True)
)
Table(
"b", metadata,
Column('id', Integer, primary_key=True),
Column('aid', ForeignKey('a.id'), nullable=True)
)
Table(
"c", metadata,
Column('id', Integer, primary_key=True),
Column('aid', ForeignKey('a.id'), nullable=False)
)
Table(
"d", metadata,
Column('id', Integer, primary_key=True),
Column(
'aid', ForeignKey('a.id', ondelete="cascade"), nullable=False)
)
Table(
"e", metadata,
Column('id', Integer, primary_key=True),
Column(
'aid', ForeignKey('a.id', ondelete="set null"),
nullable=True)
)
def test_o2m_relationship_cascade(self):
Base = automap_base(metadata=self.metadata)
Base.prepare()
configure_mappers()
b_rel = Base.classes.a.b_collection
assert not b_rel.property.cascade.delete
assert not b_rel.property.cascade.delete_orphan
assert not b_rel.property.passive_deletes
assert b_rel.property.cascade.save_update
c_rel = Base.classes.a.c_collection
assert c_rel.property.cascade.delete
assert c_rel.property.cascade.delete_orphan
assert not c_rel.property.passive_deletes
assert c_rel.property.cascade.save_update
d_rel = Base.classes.a.d_collection
assert d_rel.property.cascade.delete
assert d_rel.property.cascade.delete_orphan
assert d_rel.property.passive_deletes
assert d_rel.property.cascade.save_update
e_rel = Base.classes.a.e_collection
assert not e_rel.property.cascade.delete
assert not e_rel.property.cascade.delete_orphan
assert e_rel.property.passive_deletes
assert e_rel.property.cascade.save_update
class AutomapInhTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):
Table(
'single', metadata,
Column('id', Integer, primary_key=True),
Column('type', String(10)),
test_needs_fk=True
)
Table(
'joined_base', metadata,
Column('id', Integer, primary_key=True),
Column('type', String(10)),
test_needs_fk=True
)
Table(
'joined_inh', metadata,
Column(
'id', Integer,
ForeignKey('joined_base.id'), primary_key=True),
test_needs_fk=True
)
FixtureTest.define_tables(metadata)
def test_single_inheritance_reflect(self):
Base = automap_base()
class Single(Base):
__tablename__ = 'single'
type = Column(String)
__mapper_args__ = {
"polymorphic_identity": "u0",
"polymorphic_on": type}
class SubUser1(Single):
__mapper_args__ = {"polymorphic_identity": "u1"}
class SubUser2(Single):
__mapper_args__ = {"polymorphic_identity": "u2"}
Base.prepare(engine=testing.db, reflect=True)
assert SubUser2.__mapper__.inherits is Single.__mapper__
def test_joined_inheritance_reflect(self):
Base = automap_base()
class Joined(Base):
__tablename__ = 'joined_base'
type = Column(String)
__mapper_args__ = {
"polymorphic_identity": "u0",
"polymorphic_on": type}
class SubJoined(Joined):
__tablename__ = 'joined_inh'
__mapper_args__ = {"polymorphic_identity": "u1"}
Base.prepare(engine=testing.db, reflect=True)
assert SubJoined.__mapper__.inherits is Joined.__mapper__
assert not Joined.__mapper__.relationships
assert not SubJoined.__mapper__.relationships
def test_conditional_relationship(self):
Base = automap_base()
def _gen_relationship(*arg, **kw):
return None
Base.prepare(
engine=testing.db, reflect=True,
generate_relationship=_gen_relationship)