SQLAlchemy 尝试添加外键时出错

时间:2021-06-29 17:24:51

标签: python mysql sqlalchemy foreign-keys python-sql

我有以下脚本,如果它们在我的数据库中尚不存在,它会尝试创建 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 个答案:

答案 0 :(得分: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