joined table inheritance doesn't work with non-matching attribute names

Issue #2344 resolved
Former user created an issue

Hi SQLALchemy-Team,

I can't use joined table inheritance with non-matching attributes. See attached files which just differ in lines 16, 25, and 36. If that is a feature and not a bug, line 25 should not be necessary in the "match"-case (file 'test_ok.py'). I tested it with version 0.7.4

Cheers, Philipp

Comments (2)

  1. Mike Bayer repo owner

    This isn't a bug in my view, you're attempting to flush an EmailaddressAlias object without a primary key. alias_emailaddress is not the primary key of the object, Emailaddress.emailaddress is. From http://www.sqlalchemy.org/docs/orm/inheritance.html#joined-table-inheritance:

    One natural effect of the joined table inheritance configuration is that the identity of any mapped object can be determined entirely from the base table. This has obvious advantages, so SQLAlchemy always considers the primary key columns of a joined inheritance class to be those of the base table only, unless otherwise manually configured. In other words, the employee_id column of both the engineers and managers table is not used to locate the Engineer or Manager object itself - only the value in employees.employee_id is considered, and the primary key in this case is non-composite. engineers.employee_id and managers.employee_id are still of course critical to the proper operation of the pattern overall as they are used to locate the joined row, once the parent row has been determined, either through a distinct SELECT statement or all at once within a JOIN.
    

    The reason the PK from the ORM point of view is from the "base" table only is because it's the only part of the PK that is common to all subclasses. It's the PK from the SQL point of view because the base table is the "non-referencing" part of the condition - it's the PK without the foreign key that gets populated, the FK referencing it then gets a copy of that primary value.

    so this works:

      a = EmailaddressAlias (
          real_emailaddress="testuser@test.com",
          alias_emailaddress="tu@test.com",
          emailaddress = "tu@test.com"
          )
    

    or the other one you had.

    Line 25 in test_ok.py is necessary due to an entirely different issue, which is that your EmailaddressAlias class has two foreign keys to Emailaddress, so the inherit condition cannot be assumed. If the FK is non-ambiguous then you don't need it:

    import sqlalchemy as sa
    import sqlalchemy.orm as orm
    from sqlalchemy.ext.declarative import declarative_base
    
    Base = declarative_base()
    
    class Emailaddress(Base):
      __tablename__ = 'emailaddresses'
      emailaddress = sa.Column(sa.String, primary_key=True)
      emailtype = sa.Column(sa.String, nullable=False)
      __mapper_args__ = {'polymorphic_on': emailtype}
    
    
    class EmailaddressAlias(Emailaddress):
      __tablename__ = 'emailaddresses_alias'
      emailaddress = sa.Column(
          sa.String,
          sa.ForeignKey('emailaddresses.emailaddress'),
          primary_key=True)
      __mapper_args__ = {
          'polymorphic_identity': 'alias',
          }
    
    if __name__ == '__main__':
      engine = sa.create_engine ('sqlite:///', echo=True)
      Base.metadata.bind = engine
      Base.metadata.create_all ()
      Session = orm.sessionmaker (engine)
      session = Session ()
      a = EmailaddressAlias (
          emailaddress="tu@test.com"
          )
      session.add(a)
      session.commit()
    

    So far I'd be looking to resolve as "worksforme".

  2. Mike Bayer repo owner

    OK assuming you got this working, email on the list or ticket if you have further problems thanks !

  3. Log in to comment