diff --git a/alchy/utils.py b/alchy/utils.py index aa27001..bf5ee2d 100644 --- a/alchy/utils.py +++ b/alchy/utils.py @@ -159,6 +159,9 @@ def should_set_tablename(bases, dct): if '__tablename__' in dct or '__table__' in dct or '__abstract__' in dct: return False + if AbstractConcreteBase in bases: + return False + if has_primary_key(dct): return True @@ -170,19 +173,21 @@ def should_set_tablename(bases, dct): is_concrete = dct.get('__local_mapper_args__', {}).get('concrete', is_concrete) - for base in bases: - if base is AbstractConcreteBase: - return False + names_to_ignore = set(dct.keys()) + names_to_ignore.add('query') + for base in bases: if (not is_concrete) and (hasattr(base, '__tablename__') or hasattr(base, '__table__')): return False for name in dir(base): - if not (name in ('query') or + if not (name in names_to_ignore or (name.startswith('__') and name.endswith('__'))): attr = getattr(base, name) if getattr(attr, 'primary_key', False): return True + else: + names_to_ignore.add(name) return False diff --git a/tests/fixtures.py b/tests/fixtures.py index 0991828..b3b690f 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,5 +1,6 @@ from sqlalchemy import orm, types, Column, ForeignKey, Index +from sqlalchemy.ext.declarative import ConcreteBase, AbstractConcreteBase from sqlalchemy.ext.hybrid import hybrid_property from alchy import model, query @@ -201,6 +202,95 @@ class SearchMany(Model): search_id = Column(types.Integer(), ForeignKey('search._id')) +# Classes to test inheritance + +class AAA(Model): + __abstract__ = True + idx = Column(types.Integer(), primary_key=True) + + +class BBB(AAA): + __abstract__ = True + b_int = Column(types.Integer()) + + +class CCC(BBB): + c_int = Column(types.Integer()) + + +def get_CCC2(): + class CCC2(BBB): + idx = Column(types.Integer(), primary_key=False) + c2_int = Column(types.Integer()) + + return CCC2 + + +class DDD(CCC): + idx = Column(types.Integer(), ForeignKey(CCC.idx), + primary_key=True) + d_int = Column(types.Integer()) + + +class EEE(BBB): + idx = Column(types.Integer(), primary_key=True) + e_str = Column(types.String()) + __global_mapper_args__ = {'polymorphic_on': e_str} + + +class FFF(EEE): + f_int = Column(types.Integer()) + __local_mapper_args__ = {'polymorphic_identity': 'eee_subtype_fff'} + + +class FFF2(EEE): + f2_int = Column(types.Integer()) + __mapper_args__ = {'polymorphic_identity': 'eee_subtype_fff2'} + + +# Concrete table inheritance +class GGG(CCC): + idx = Column(types.Integer(), primary_key=True) + g_int = Column(types.Integer()) + __local_mapper_args__ = {'concrete': True} + + +# Concrete table inheritance - using ConcreteBase +class HHH(ConcreteBase, BBB): + h_int = Column(types.Integer()) + __local_mapper_args__ = {'polymorphic_on': h_int, 'concrete': True} + + +class III(HHH): + idx = Column(types.Integer(), primary_key=True) + i_int = Column(types.Integer()) + __mapper_args__ = {'polymorphic_identity': 2, 'concrete': True} + + +# Concrete table inheritance - using AbstractConcreteBase +class JJJ(AbstractConcreteBase, Model): + idx = Column(types.Integer(), primary_key=True) + j_int = Column(types.Integer()) + __local_mapper_args__ = {'polymorphic_on': j_int} + + +class KKK(JJJ): + idx = Column(types.Integer(), primary_key=True) + k_int = Column(types.Integer()) + __mapper_args__ = {'polymorphic_identity': 2, 'concrete': True} + + +class LLL(AbstractConcreteBase, Model): + l_int = Column(types.Integer()) + __local_mapper_args__ = {'polymorphic_on': l_int} + + +class MMM(LLL): + idx = Column(types.Integer(), primary_key=True) + m_int = Column(types.Integer()) + __mapper_args__ = {'polymorphic_identity': 3, 'concrete': True} + + Models = { 'Foo': Foo, 'Bar': Bar, diff --git a/tests/test_model.py b/tests/test_model.py index daba6b1..495eeb8 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,7 +1,7 @@ -from sqlalchemy import orm, Column, types, inspect, Index, ForeignKey +from sqlalchemy import orm, Column, types, inspect, Index from sqlalchemy.orm.exc import UnmappedClassError -from sqlalchemy.ext.declarative import ConcreteBase, AbstractConcreteBase +from sqlalchemy.exc import InvalidRequestError from alchy import query @@ -542,82 +542,21 @@ def test_is_modified(self): self.assertEqual(record.is_modified(), False) def test_should_set_tablename(self): - class AAA(Model): - __abstract__ = True - idx = Column(types.Integer(), primary_key=True) + from .fixtures import (AAA, BBB, CCC, get_CCC2, DDD, EEE, FFF, FFF2, + GGG, HHH, III, JJJ, KKK, LLL, MMM) self.assertEqual(hasattr(AAA, '__tablename__'), False) - - class BBB(AAA): - __abstract__ = True - b_int = Column(types.Integer()) - self.assertEqual(hasattr(BBB, '__tablename__'), False) - - class CCC(BBB): - c_int = Column(types.Integer()) - self.assertEqual(getattr(CCC, '__tablename__'), 'ccc') - - # Joined table inheritance - class DDD(CCC): - idx = Column(types.Integer(), ForeignKey(CCC.idx), - primary_key=True) - d_int = Column(types.Integer()) - + self.assertRaises(InvalidRequestError, get_CCC2) self.assertEqual(getattr(DDD, '__tablename__'), 'ddd') - - # Single table inheritance - class EEE(BBB): - idx = Column(types.Integer(), primary_key=True) - e_str = Column(types.String()) - __global_mapper_args__ = {'polymorphic_on': e_str} - self.assertEqual(getattr(EEE, '__tablename__'), 'eee') - - class FFF(EEE): - f_int = Column(types.Integer()) - __local_mapper_args__ = {'polymorphic_identity': 'eee_subtype_fff'} - self.assertEqual(getattr(FFF, '__tablename__'), 'eee') - - class FFF2(EEE): - f2_int = Column(types.Integer()) - __mapper_args__ = {'polymorphic_identity': 'eee_subtype_fff2'} - self.assertEqual(getattr(FFF2, '__tablename__'), 'eee') - - # Concrete table inheritance - class GGG(CCC): - idx = Column(types.Integer(), primary_key=True) - g_int = Column(types.Integer()) - __local_mapper_args__ = {'concrete': True} - self.assertEqual(getattr(GGG, '__tablename__'), 'ggg') - - # Concrete table inheritance - using ConcreteBase - class HHH(ConcreteBase, BBB): - h_int = Column(types.Integer()) - __local_mapper_args__ = {'polymorphic_on': h_int, 'concrete': True} - self.assertEqual(getattr(HHH, '__tablename__'), 'hhh') - - class III(HHH): - idx = Column(types.Integer(), primary_key=True) - i_int = Column(types.Integer()) - __mapper_args__ = {'polymorphic_identity': 2, 'concrete': True} - self.assertEqual(getattr(III, '__tablename__'), 'iii') - - # Concrete table inheritance - using AbstractConcreteBase - class JJJ(AbstractConcreteBase, BBB): - pass - self.assertEqual(hasattr(JJJ, '__tablename__'), False) - - class KKK(JJJ): - idx = Column(types.Integer(), primary_key=True) - k_int = Column(types.Integer()) - __mapper_args__ = {'polymorphic_identity': 2, 'concrete': True} - self.assertEqual(getattr(KKK, '__tablename__'), 'kkk') + self.assertEqual(hasattr(LLL, '__tablename__'), False) + self.assertEqual(getattr(MMM, '__tablename__'), 'mmm')