简体   繁体   中英

SqlAlchemy Foreign Key Error on Many to Many Relationship

I am attempting to set up a basic many-to-many relationship in SqlAlchemy. I am able to successfully create the database using db.create_all() but I run into an error when trying to retrieve any records from the database.

class Geo(db.Model):
    __tablename__ = 'geos'

    geo = db.Column(db.Integer, primary_key=True)
    states = db.relationship("State", secondary='geos_states')



class State(db.Model):
    __tablename__ = 'states'

    state_short = db.Column(db.String, primary_key=True)
    geos = db.relationship("Geo", secondary='geos_states')



class GeoState(db.Model):
    __tablename__ = 'geos_states'

    id = db.Column(db.Integer, primary_key=True)
    geo_id = db.Column(db.Integer, db.ForeignKey('geos.geo')),
    state_short_id = db.Column(db.String, db.ForeignKey('states.state_short'))

The error is as follows:

sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers.  Original exception was: Could not determine join condition between parent/child tables on relationship Geo.states - there are no foreign keys linking these tables via secondary table 'geos_states'.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify 'primaryjoin' and 'secondaryjoin' expressions.

The pattern you are using is a variant on the many-many relationship pattern in SQLAlchemy.

Typically what you would do is the following

geostates_association_table = Table(
    'geo_states', Base.metadata,
    db.Column(
        'geo_id', db.Integer, 
        db.ForeignKey('geos.geo')
    ),
    db.Column(
        'state_short_id', db.String,
        db.ForeignKey('states.state_short')
    )
)
    

class Geo(db.Model):
    __tablename__ = 'geos'

    geo = db.Column(db.Integer, primary_key=True)
    states = db.relationship(
        "State",
        secondary= geostates_association_table,
        back_populates='geos'
    )

class State(db.Model):
    __tablename__ = 'states'

    state_short = db.Column(
        db.String, 
        primary_key=True
    )
    geos = db.relationship(
       "Geo", 
       secondary=geostates_association_table,
       back_populates='states'
    )

I think this what you need since you don't seem to have any additional columns required on the mapping table in the many-many relationship. However, if you did you would need to use the Association Object pattern which is what you are doing originally. In this case

class Geo(db.Model):
    __tablename__ = 'geos'

    geo = db.Column(
        db.Integer,
        primary_key=True
    )
    states = db.relationship(
        "GeoState",
        back_populates="geos"
    )



class State(db.Model):
    __tablename__ = 'states'

    state_short = db.Column(
        db.String, 
        primary_key=True
    )
    geos = db.relationship(
        "GeoState",
         back_populates="states"
    )



class GeoState(db.Model):
    __tablename__ = 'geos_states'
    # Notice how we set primary keys on both the fields
    geo_id = db.Column(
        db.Integer,
        db.ForeignKey('geos.geo'),
        primary_key=True
    ),
    state_short_id = db.Column(
        db.String,
        db.ForeignKey('states.state_short'),
        primary_key=True
    ),
    some_other_field = db.Column(
       db.String
    )

    geos = relationship("Geo", back_populates="states")
    states = relationship("State", back_populates="geos")

Note that in this case what you'll get in your relationship lists for State and Geo will actually be GeoState objects. If you want to use the Association Object pattern but have the relationship lists be populated by their respective counterpart objects instead. You can use the AssociationProxy sqlalchemy extension which you can find the documentation for here

For your reference,

The Association Object pattern is documented here

Basic Many to Many pattern is documented here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM