zer0hedge
zer0hedge

Reputation: 479

SQLAlchemy can't join two tables with two foreign keys between them

The code below does not work due to this line owner_id = Column(Integer, ForeignKey('employees.employee_id')) in Manager class. SQLAlchemy generates error message:

AmbiguousForeignKeysError: Can't determine join between 'employees' and > 'managers'; tables have more than one foreign key constraint relationship > between them. Please specify the 'onclause' of this join explicitly.

Please help to fix that!

The idea is that every Manager is an Employee and works for some Owner. There might be zero, one or more Managers working for an Owner.

from sqlalchemy import (Table, Column, Integer, String, create_engine,
    MetaData, ForeignKey)
from sqlalchemy.orm import mapper, create_session
from sqlalchemy.ext.declarative import declarative_base

e = create_engine('sqlite:////tmp/foo.db', echo=True)
Base = declarative_base(bind=e)

class Employee(Base):
    __tablename__ = 'employees'

    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(30), nullable=False)

    __mapper_args__ = {'polymorphic_on': type}

    def __init__(self, name):
        self.name = name

class Manager(Employee):
    __tablename__ = 'managers'
    __mapper_args__ = {'polymorphic_identity': 'manager'}

    employee_id = Column(Integer, ForeignKey('employees.employee_id'),
                         primary_key=True)
    manager_data = Column(String(50))

    owner_id = Column(Integer, ForeignKey('employees.employee_id'))


    def __init__(self, name, manager_data):
        super(Manager, self).__init__(name)
        self.manager_data = manager_data

class Owner(Manager):
    __tablename__ = 'owners'
    __mapper_args__ = {'polymorphic_identity': 'owner'}

    employee_id = Column(Integer, ForeignKey('managers.employee_id'),
                         primary_key=True)
    owner_secret = Column(String(50))

    def __init__(self, name, manager_data, owner_secret):
        super(Owner, self).__init__(name, manager_data)
        self.owner_secret = owner_secret

Base.metadata.drop_all()
Base.metadata.create_all()

s = create_session(bind=e, autoflush=True, autocommit=False)    
o = Owner('nosklo', 'mgr001', 'ownerpwd')
s.add(o)
s.commit()

Upvotes: 3

Views: 5140

Answers (2)

univerio
univerio

Reputation: 20508

SQLAlchemy is confused about how to join Manager to Employee because you have multiple foreign keys between the two tables, employee_id and owner_id. In this case, you need to specify the inherit_condition to the mapper explicitly:

class Manager(Employee):
    __tablename__ = 'managers'

    employee_id = Column(Integer, ForeignKey('employees.employee_id'),
                         primary_key=True)
    manager_data = Column(String(50))

    owner_id = Column(Integer, ForeignKey('employees.employee_id'))

    __mapper_args__ = {'polymorphic_identity': 'manager', 'inherit_condition': employee_id == Employee.employee_id}

Upvotes: 5

user1711330
user1711330

Reputation: 54

You are both extending parent model as well as defining relationship to the same parent model.

simply use one way, you could simply created relations and have all your models extend base class

class Employee(Base):
    __tablename__ = 'employees'

    employee_id = Column(Integer, primary_key=True)
    ...
class Owner(Base):
    __tablename__ = 'owners'
    __mapper_args__ = {'polymorphic_identity': 'owner'}
    owner_id = Column(Integer, primary_key=True)
    ...
    ...
class Manager(Base):
    __tablename__ = 'managers'
    __mapper_args__ = {'polymorphic_identity': 'manager'}

    manager_id = Column(Integer, primary_key=True)
    employee_id = Column(Integer, ForeignKey('employees.employee_id'), primary_key=True)
    owner_id = Column(Integer, ForeignKey('owners.owner_id'))
    ...
    ...

add the rest of relevant field where indicated as ....

Upvotes: -1

Related Questions