SQLAlchemy one-to-one, store foreign key in each table?

Question:

I have a relationship that is one to one between cage codes and duns numbers.

I have set up my relationship that looks like, where I store a ForeignKey on each of the respective tables.

class Cage(Base):
    __tablename__ = 'DimCage'

    id = Column(Integer, primary_key=True)
    cage = Column(String(8), unique=True, nullable=False)
    duns_id = Column(Integer, ForeignKey('DimDuns.id'))

    duns = relationship('Duns', uselist=False, back_populates='cage')


class Duns(Base):
    __tablename__ = 'DimDuns'

    id = Column(Integer, primary_key=True)
    duns = Column(String(10), unique=True, nullable=False)
    dunsInt = Column(Integer, unique=True, nullable=False)
    cage_id = Column(Integer, ForeignKey('DimCage.id'))

    cage = relationship('Cage', uselist=False, back_populates='duns')

When I create the tables I get the below error, how to do I set up my foreign keys so I can keep a reference on both tables?

sqlalchemy.exc.AmbiguousForeignKeysError: Can’t determine join between ‘DimCage’ and ‘DimDuns’; tables have more than one foreign key constraint relationship between them. Please specify the ‘onclause’ of this join explicitly.

And During handling of the above exception, another exception occurred:

sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Cage.duns – there are multiple foreign key paths linking the tables. Specify the ‘foreign_keys’ argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.

Asked By: spitfiredd

||

Answers:

I believe you only need to store one foreign key for a one-to-one relationship.
See https://docs.sqlalchemy.org/en/13/orm/basic_relationships.html#one-to-one

You shouldn’t lose any data access this way. If you remove the duns_id column from Cage, you now access the id by cage.duns.id instead of cage.duns_id.

Answered By: Brian Popeck

You could set the child primary key as a foreign key to the parent.

For a design perspective, it is not a bad choice to use foreign keys as primary keys in One-to-One relationships.

class Cage(Base):
    __tablename__ = 'DimCage'

    id = Column(Integer, primary_key=True)
    cage = Column(String(8), unique=True, nullable=False)


class Duns(Base):
    __tablename__ = 'DimDuns'

    id = Column(Integer, ForeignKey(Cage.id) primary_key=True)
    
    duns = Column(String(10), unique=True, nullable=False)
    dunsInt = Column(Integer, unique=True, nullable=False)

    cage = relationship('Cage', uselist=False, backref='duns')

Now both cage and duns have the same id. So:

session.add(Cage(id=1, duns=Duns(duns='Duns', dunsInt=10)))
sesion.commit()

id = 1

cage = select(Cage).where(Cage.id == id)

duns = select(Duns).where(Duns.id == cage.id)

assert cage.id == duns.id

Please note that the child cannot exist without the parent.

If the parent is going to be deleted then the child must be deleted first, unless you configure some cascade option.

Answered By: Ramon Dias
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.