`has_inherited_table` should probably skip checking the current class

Issue #2656 resolved
Sok Ann Yap created an issue

The has_inherited_table checking is done using for-loop:

for class_ in cls.__mro__:

I think this should be changed to:

for class_ in cls.__mro__[1:](1:):

Otherwise, the function seems to be only useful during the class mapping stage.

Here's a slightly modified code sample based on the example at http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#controlling-table-inheritance-with-mixins:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import has_inherited_table
from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, String

Base = declarative_base()

class Tablename(object):
    @declared_attr
    def __tablename__(cls):
        print cls.__name__, has_inherited_table(cls)
        if has_inherited_table(cls):
            return None
        return cls.__name__.lower()

class Person(Tablename, Base):
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class Engineer(Person):
    primary_language = Column(String(50))
    __mapper_args__ = {'polymorphic_identity': 'engineer'}

for cls in (Person, Engineer):
    print cls.__name__, has_inherited_table(cls)

With cls.__mro__, the output is:

Person False
Engineer True
Person True
Engineer True

With cls.__mro__[1:](1:), the output is:

Person False
Engineer True
Person False
Engineer True

Comments (5)

  1. Mike Bayer repo owner

    hmmm OK , well the function was only intended to be used during the class mapping stage, but if you want to use it elsewhere, I can make this change for 0.8 hows that ?

  2. Sok Ann Yap reporter

    Cool. I was going to use it in a Table's after_create event, which I added for all the model classes (DB2 for z/OS has this annoying behavior such that if a table is created in an explicitly specified tablespace, a unique index has to be created separately for each primary key / unique constraint)

  3. Mike Bayer repo owner

    unfortunately, this is actually a regression, wish I knew this before we released:

    from sqlalchemy import *
    from sqlalchemy.orm import configure_mappers
    from sqlalchemy.ext.declarative import (declarative_base,
        declared_attr, has_inherited_table)
    
    Base = declarative_base()
    
    class Test(Base):
        __tablename__ = 'test'
        id = Column(Integer, primary_key=True)
        type = Column(String(20))
    
        @declared_attr
        def __mapper_args__(cls):
            if not has_inherited_table(cls):
                ret = {
                    'polymorphic_identity': 'default',
                    'polymorphic_on': cls.type,
                    }
            else:
                ret = {'polymorphic_identity': cls.__name__}
            return ret
    
    class PolyTest(Test):
        __tablename__ = 'poly_test'
        id = Column(Integer, ForeignKey(Test.id), primary_key=True)
    
    configure_mappers()
    
    assert Test.__mapper__.polymorphic_on is Test.__table__.c.type
    assert PolyTest.__mapper__.polymorphic_on is Test.__table__.c.type
    
  4. Log in to comment