Sqlalchemy utilize foreign key in table creation
Question:
I’ve began learning sqlalchemy and the script below shows how far I’ve gotten. I’ve created a class to start the database, a class that creates a table containing games and their ID’s and a class that is used to create tables for each individual external data source.
I realized I need to incorporate foreign keys but upon doing so I get the error below. It’s very confusing because I’m fairly certain the MLBGamelist table was created. Any help on this wuld be greatly appreciated.
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'CaesarsGamelist.game_ref' could not find table 'MLBGamelist' with which to generate a foreign key to target column 'game_ref'
class DBControl:
def __init__(self,mem):
print('>>>> [MAIN]: INITIALIZING MAIN DATABASE CONNECTION')
self.engine = create_engine(memory[mem], echo=False)
self.inspector = inspect(self.engine)
self.db_connection = self.engine.connect()
self.create_session = sessionmaker(bind=self.engine)
class GamelistMLBControl:
def __init__(self,book,db_control):
self.table_name = f'{book}Gamelist'
self.db_control = db_control
def commit_entry(self,site_data):
write_session = scoped_session(self.db_control.create_session)
insert_stmt = insert(self.check_table()).values(site_data)
write_session.execute(insert_stmt)
write_session.commit()
write_session.remove()
def check_table(self):
metadata = MetaData(bind=self.db_control.engine)
if self.table_name not in self.db_control.inspector.get_table_names():
table_name = Table(
str(self.table_name),
metadata,
Column("event_id", Integer, primary_key=True),
Column("game_ref", String),
Column("game_datetime", Integer),
Column("book", String),
)
metadata.create_all(self.db_control.db_connection)
print(f'> [{self.table_name}]: Table created')
else:
metadata.reflect(self.db_control.engine)
print(f'> [{self.table_name}]: Table exists')
return Table(table_name, metadata, autoload=True)
class GamelistControl:
def __init__(self,book,db_control):
self.table_name = f'{book}Gamelist'
self.db_control = db_control
def commit_entry(self,site_data):
write_session = scoped_session(self.db_control.create_session)
insert_stmt = insert(self.check_table()).values(site_data)
write_session.execute(insert_stmt)
write_session.commit()
write_session.remove()
def check_table(self):
metadata = MetaData(bind=self.db_control.engine)
if self.table_name not in self.db_control.inspector.get_table_names():
table_name = Table(
str(self.table_name),
metadata,
Column("event_id", Integer, primary_key=True),
Column("game_ref", String, ForeignKey('MLBGamelist.game_ref')),
Column("game_datetime", Integer, ForeignKey('MLBGamelist.game_datetime')),
Column("book", String),
)
metadata.create_all(self.db_control.db_connection)
print(f'> [{self.table_name}]: Table created')
else:
metadata.reflect(self.db_control.engine)
print(f'> [{self.table_name}]: Table exists')
return Table(table_name, metadata, autoload=True)
Answers:
SQLAlchemy stores details of tables in MetaData
objects when a table is created or reflected. Binding an engine to a MetaData
object does not in itself cause any table details to be stored. In the code in the question, each class creates a separate MetaData
instance, hence the error occurs because the Metadata
instance created in GamelistControl
doesn’t have details of the table created in GamelistMLBControl
.
To solve the problem, either call metadata.reflect()
after creating the MetaData
instance in GamelistControl
or create a single MetaData
as an attribute of DBControl
and use that to create tables in both classes.
I’ve began learning sqlalchemy and the script below shows how far I’ve gotten. I’ve created a class to start the database, a class that creates a table containing games and their ID’s and a class that is used to create tables for each individual external data source.
I realized I need to incorporate foreign keys but upon doing so I get the error below. It’s very confusing because I’m fairly certain the MLBGamelist table was created. Any help on this wuld be greatly appreciated.
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'CaesarsGamelist.game_ref' could not find table 'MLBGamelist' with which to generate a foreign key to target column 'game_ref'
class DBControl:
def __init__(self,mem):
print('>>>> [MAIN]: INITIALIZING MAIN DATABASE CONNECTION')
self.engine = create_engine(memory[mem], echo=False)
self.inspector = inspect(self.engine)
self.db_connection = self.engine.connect()
self.create_session = sessionmaker(bind=self.engine)
class GamelistMLBControl:
def __init__(self,book,db_control):
self.table_name = f'{book}Gamelist'
self.db_control = db_control
def commit_entry(self,site_data):
write_session = scoped_session(self.db_control.create_session)
insert_stmt = insert(self.check_table()).values(site_data)
write_session.execute(insert_stmt)
write_session.commit()
write_session.remove()
def check_table(self):
metadata = MetaData(bind=self.db_control.engine)
if self.table_name not in self.db_control.inspector.get_table_names():
table_name = Table(
str(self.table_name),
metadata,
Column("event_id", Integer, primary_key=True),
Column("game_ref", String),
Column("game_datetime", Integer),
Column("book", String),
)
metadata.create_all(self.db_control.db_connection)
print(f'> [{self.table_name}]: Table created')
else:
metadata.reflect(self.db_control.engine)
print(f'> [{self.table_name}]: Table exists')
return Table(table_name, metadata, autoload=True)
class GamelistControl:
def __init__(self,book,db_control):
self.table_name = f'{book}Gamelist'
self.db_control = db_control
def commit_entry(self,site_data):
write_session = scoped_session(self.db_control.create_session)
insert_stmt = insert(self.check_table()).values(site_data)
write_session.execute(insert_stmt)
write_session.commit()
write_session.remove()
def check_table(self):
metadata = MetaData(bind=self.db_control.engine)
if self.table_name not in self.db_control.inspector.get_table_names():
table_name = Table(
str(self.table_name),
metadata,
Column("event_id", Integer, primary_key=True),
Column("game_ref", String, ForeignKey('MLBGamelist.game_ref')),
Column("game_datetime", Integer, ForeignKey('MLBGamelist.game_datetime')),
Column("book", String),
)
metadata.create_all(self.db_control.db_connection)
print(f'> [{self.table_name}]: Table created')
else:
metadata.reflect(self.db_control.engine)
print(f'> [{self.table_name}]: Table exists')
return Table(table_name, metadata, autoload=True)
SQLAlchemy stores details of tables in MetaData
objects when a table is created or reflected. Binding an engine to a MetaData
object does not in itself cause any table details to be stored. In the code in the question, each class creates a separate MetaData
instance, hence the error occurs because the Metadata
instance created in GamelistControl
doesn’t have details of the table created in GamelistMLBControl
.
To solve the problem, either call metadata.reflect()
after creating the MetaData
instance in GamelistControl
or create a single MetaData
as an attribute of DBControl
and use that to create tables in both classes.