SQLAlchemy 尝试添加外键时出错
SQLAlchemy Error Trying To Add Foreign Key
我有以下脚本,它会尝试创建 3 个 table,如果它们不存在于我的数据库中。
我正在使用 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'
我希望第一个 table (trade_id) 的主键用作其他两个 table 的外键。
我还看到了其他构造 table 的方法,主要是结合 Flask,创建模型 class 的子 class 并填写详细信息table 那里,构建这样一个小型数据库应用程序的更正确方法是什么?
主键出现问题是因为 MetaData
对象是一系列 table 的存储对象。定义外键时,它会查看 MetaData
对象,以便将相关的 table 映射到。当您在不同的创建函数中重新定义 MetaData
对象时,它们都存储在不同的 MetaData
对象中。因此,在创建与第一个 table.
相关的第二个 table 期间无法查找外键
解决方案是定义一次MetaData
对象,然后将每个Table
对象引用到这个MetaData
对象。
有关此的更多信息,请参阅 Working with Database Metadata。
此外,您不必在每个创建函数中都调用 create_all
,但可以在 __init__
结束时调用一次。
关于你最后一个关于不同方法的问题,你的方法主要是SQLAlchemy Core。使用 Base
的子类时,您将进入 SQLAlchemy ORM。这里对差异进行了更多解释:What is the difference between SQLAlchemy Core and ORM
我有以下脚本,它会尝试创建 3 个 table,如果它们不存在于我的数据库中。
我正在使用 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'
我希望第一个 table (trade_id) 的主键用作其他两个 table 的外键。
我还看到了其他构造 table 的方法,主要是结合 Flask,创建模型 class 的子 class 并填写详细信息table 那里,构建这样一个小型数据库应用程序的更正确方法是什么?
主键出现问题是因为 MetaData
对象是一系列 table 的存储对象。定义外键时,它会查看 MetaData
对象,以便将相关的 table 映射到。当您在不同的创建函数中重新定义 MetaData
对象时,它们都存储在不同的 MetaData
对象中。因此,在创建与第一个 table.
解决方案是定义一次MetaData
对象,然后将每个Table
对象引用到这个MetaData
对象。
有关此的更多信息,请参阅 Working with Database Metadata。
此外,您不必在每个创建函数中都调用 create_all
,但可以在 __init__
结束时调用一次。
关于你最后一个关于不同方法的问题,你的方法主要是SQLAlchemy Core。使用 Base
的子类时,您将进入 SQLAlchemy ORM。这里对差异进行了更多解释:What is the difference between SQLAlchemy Core and ORM