Eli
Eli

Reputation: 39009

Specify join condition in SQLalchemy ORM without foreign key

I have two models in SQLAlchemy that I had automatically joining on a foreign key, like so:

class Parent(Base):
    __tablename__ = 'parents'

    id = Column(Integer, primary_key=True)
    name = Column(String(300), nullable=False)
    metadata_id = Column(Integer, nullable=True, index=True)

class Child(Base):
    __tablename__ = 'children'

    id = Column(Integer, primary_key=True)
    name = Column(String(300), nullable=False)
    parent_metadata_id = \
        Column(ForeignKey('parents.metadata_id'),
               nullable=True, primary_key=True)
    parent = relationship(u'Parent')

This worked fine and I could get easily access a parent from its children. Now, for technical reasons beyond the scope of this question, I had to get rid of the foreign key in my db. I've tried to get around that in SQLAlchemy, but none of the replacement code (using primaryjoins, or backrefs) has worked. I saw another answer here that just says to lie to SQLAlchemy and tell it I have the foreign key relationship, but this makes Alembic try to create the foreign key relationship on every new revision I autogenerate, which is really annoying. What's the right way to do this?

Upvotes: 1

Views: 8516

Answers (1)

univerio
univerio

Reputation: 20548

To make the relationship work, you can specify the explicit join condition:

parent = relationship(Parent, primaryjoin=parent_metadata_id == Parent.metadata_id)

To make joins to Child work, you can specify the relationship instead of the entity:

session.query(Child).join(Child.parent)

Or, specify the join condition explicitly:

session.query(Child).join(Child, Child.parent_metadata_id == Parent.metadata_id)

Lying to SQLAlchemy also works. You can make alembic ignore the foreign key by specifying the include_object parameter:

class Child(Base):
    ...
    parent_metadata_id = Column(ForeignKey( ... ), info={"skip_autogenerate": True}, ...)

def include_object(object, name, type_, reflected, compare_to):
    if not reflected and object.info.get("skip_autogenerate", False):
        return False
    return True

Upvotes: 8

Related Questions