web-dev-qa-db-fra.com

SQLAlchemy plusieurs clés étrangères dans une classe mappée à la même clé primaire

J'essaie de configurer une table postgresql qui a deux clés étrangères qui pointent vers la même clé primaire dans une autre table.

Lorsque j'exécute le script, j'obtiens l'erreur

sqlalchemy.exc.AmbiguousForeignKeysError: impossible de déterminer la condition de jointure entre les tables parent/enfant sur la relation Company.stakeholder - il existe plusieurs chemins d'accès de clé étrangère reliant les tables. Spécifiez l'argument 'foreign_keys', en fournissant une liste des colonnes qui doivent être comptées comme contenant une référence de clé étrangère vers la table parent.

C'est l'erreur exacte dans la Documentation SQLAlchemy pourtant quand je réplique ce qu'ils ont proposé comme solution, l'erreur ne disparaît pas. Que pourrais-je faire de mal?

#The business case here is that a company can be a stakeholder in another company.
class Company(Base):
    __table= 'company'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)

class Stakeholder(Base):
    __table= 'stakeholder'
    id = Column(Integer, primary_key=True)
    company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
    stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
    company = relationship("Company", foreign_keys='company_id')
    stakeholder = relationship("Company", foreign_keys='stakeholder_id')

J'ai vu des questions similaires ici mais certaines des réponses recommandez que l'on utilise un primaryjoin pourtant dans la documentation il indique que vous n'avez pas besoin du primaryjoin dans cette situation .

35
lukik

J'ai essayé de supprimer les guillemets des Foreign_keys et d'en faire une liste. De la documentation officielle sur Relationship Configuration: Handling Multiple Join Paths

Modifié dans la version 0.8: relationship() peut résoudre l'ambiguïté entre les cibles de clés étrangères sur la seule base de l'argument foreign_keys; l'argument primaryjoin n'est plus nécessaire dans cette situation.


Le code autonome ci-dessous fonctionne avec sqlalchemy>=0.9:

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(u'sqlite:///:memory:', echo=True)
session = scoped_session(sessionmaker(bind=engine))
Base = declarative_base()

#The business case here is that a company can be a stakeholder in another company.
class Company(Base):
    __table= 'company'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)

class Stakeholder(Base):
    __table= 'stakeholder'
    id = Column(Integer, primary_key=True)
    company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
    stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
    company = relationship("Company", foreign_keys=[company_id])
    stakeholder = relationship("Company", foreign_keys=[stakeholder_id])

Base.metadata.create_all(engine)

# simple query test
q1 = session.query(Company).all()
q2 = session.query(Stakeholder).all()
45
van

La dernière documentation:

La forme de foreign_keys= dans la documentation produit une erreur NameError, je ne sais pas comment elle devrait fonctionner lorsque la classe n'a pas encore été créée. Avec du piratage, j'ai réussi à réussir:

company_id = Column(Integer, ForeignKey('company.id'), nullable=False)
company = relationship("Company", foreign_keys='Stakeholder.company_id')

stakeholder_id = Column(Integer, ForeignKey('company.id'), nullable=False)
stakeholder = relationship("Company",
                            foreign_keys='Stakeholder.stakeholder_id')

En d'autres termes:

… foreign_keys='CurrentClass.thing_id')
3
Gringo Suave