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.