尝试添加外键的 SQLAlchemy 错误
Posted
技术标签:
【中文标题】尝试添加外键的 SQLAlchemy 错误【英文标题】:SQLAlchemy Error Trying To Add Foreign Key 【发布时间】:2021-09-11 22:15:29 【问题描述】:我有以下脚本,如果它们在我的数据库中尚不存在,它会尝试创建 3 个表。
我使用 mysql 作为数据库引擎。
class Journal:
USER = '******'
PASSWORD = '******'
HOST = '******'
DB_NAME = 'trades_test'
def __init__(self):
self.engine = create_engine(
f'mysql+mysqlconnector://self.USER:self.PASSWORD@self.HOST/self.DB_NAME'
)
self.create_openings_table()
self.create_closings_table()
self.create_adjustments_table()
def create_openings_table(self):
meta = MetaData(self.engine)
self.openings = Table(
'openings',
meta,
Column('trade_id',
INTEGER(unsigned=True),
primary_key=True,
autoincrement=True),
Column('opened_at', DATE(), nullable=False),
Column('underlying', VARCHAR(5), nullable=False),
Column('underlying_price', FLOAT(2), nullable=False),
Column('iv_rank', SMALLINT(), nullable=False),
Column('strategy', VARCHAR(20), nullable=False),
Column('quantity', SMALLINT(), nullable=False),
Column('expiration_date', DATE(), nullable=False),
Column('option_types', JSON()),
Column('strikes', JSON(), nullable=False),
Column('premium', FLOAT(2), nullable=False),
Column('prob_of_profit', FLOAT(2), nullable=False),
Column('margin', FLOAT(2), nullable=False),
Column('notes', TEXT()))
meta.create_all()
def create_closings_table(self):
meta = MetaData(self.engine)
self.closings = Table(
'closings',
meta,
Column('id',
INTEGER(unsigned=True),
primary_key=True,
autoincrement=True),
# FOREIGN KEY - fk_closings_trade_id
Column('trade_id', ForeignKey('openings.trade_id')),
Column('closed_at', DATE(), nullable=False),
Column('underlying_price', FLOAT(2), nullable=False),
Column('iv_rank', SMALLINT(), nullable=False),
Column('premium', FLOAT(2), nullable=False),
Column('margin', FLOAT(2), nullable=False),
Column('notes', TEXT()),
)
meta.create_all()
def create_adjustments_table(self):
meta = MetaData(self.engine)
self.adjustments = Table(
'adjustments',
meta,
Column('id',
INTEGER(unsigned=True),
primary_key=True,
autoincrement=True),
# FOREIGN KEY - fk_adj_trade_id
Column('trade_id', ForeignKey('openings.trade_id')),
Column('adjusted_at', DATE(), nullable=False),
Column('underlying_price', FLOAT(2), nullable=False),
Column('iv_rank', SMALLINT(), nullable=False),
Column('quantity', SMALLINT()),
Column('premium', FLOAT(2)),
Column('option_types', JSON()),
Column('strikes', JSON()),
Column('expiration_date', DATE()),
Column('margin', FLOAT(2)),
Column('notes', TEXT()),
)
meta.create_all()
此代码产生此错误:
Traceback (most recent call last):
File "/Users/or/Desktop/Or/Options/journal/journal.py", line 105, in <module>
Journal()
File "/Users/or/Desktop/Or/Options/journal/journal.py", line 16, in __init__
self.create_closings_table()
File "/Users/or/Desktop/Or/Options/journal/journal.py", line 61, in create_closings_table
meta.create_all()
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 4744, in create_all
bind._run_ddl_visitor(
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 3008, in _run_ddl_visitor
conn._run_ddl_visitor(visitorcallable, element, **kwargs)
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 2016, in _run_ddl_visitor
visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/visitors.py", line 483, in traverse_single
return meth(obj, **kw)
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/ddl.py", line 822, in visit_metadata
collection = sort_tables_and_constraints(
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/ddl.py", line 1286, in sort_tables_and_constraints
dependent_on = fkc.referred_table
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 3671, in referred_table
return self.elements[0].column.table
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 1093, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "/Users/or/opt/anaconda3/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 2376, in column
raise exc.NoReferencedTableError(
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'closings.trade_id' could not find table 'openings' with which to generate a foreign key to target column 'trade_id'
我希望第一个表 (trade_id) 的主键用作其他两个表的外键。
我还看到了其他构造表的方法,主要是结合 Flask,创建 Model 类的子类并在那里填写表的详细信息,构造小型数据库应用程序的更正确方法是什么像这样?
【问题讨论】:
【参考方案1】:出现主键问题是因为MetaData
对象是一系列表的存储对象。定义外键时,它会查看 MetaData
对象,以便映射到相关表。当您在不同的创建函数中重新定义 MetaData
对象时,它们都存储在不同的 MetaData
对象中。因此,在创建与第一个表有关系的第二个表期间无法查找外键。
解决方案是定义一次MetaData
对象,并将每个Table
对象引用到这个MetaData
对象。
有关更多信息,请参阅Working with Database Metadata。
您也不必在每个创建函数中调用create_all
,但可以在__init__
的末尾调用一次。
关于你关于不同方法的最后一个问题是你的方法主要是 SQLAlchemy Core。当使用Base
的子类时,您将进入 SQLAlchemy ORM。这里稍微解释一下差异:What is the difference between SQLAlchemy Core and ORM
【讨论】:
我把它们都放在一个功能下,效果很好,感谢您的出色回答。以上是关于尝试添加外键的 SQLAlchemy 错误的主要内容,如果未能解决你的问题,请参考以下文章