微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

SQLAlchemy 尝试添加外键时出错

如何解决SQLAlchemy 尝试添加外键时出错

我有以下脚本,如果它们在我的数据库中尚不存在,它会尝试创建 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),Column('underlying_price',FLOAT(2),Column('iv_rank',SMALLINT(),Column('strategy',VARCHAR(20),Column('quantity',Column('expiration_date',Column('option_types',JSON()),Column('strikes',JSON(),Column('premium',Column('prob_of_profit',Column('margin',Column('notes',TEXT()))
            Meta.create_all()
    
        def create_closings_table(self):
            Meta = MetaData(self.engine)
            self.closings = Table(
                'closings',Column('id',# FOREIGN KEY - fk_closings_Trade_id
                Column('Trade_id',ForeignKey('openings.Trade_id')),Column('closed_at',TEXT()),)
            Meta.create_all()
    
        def create_adjustments_table(self):
            Meta = MetaData(self.engine)
            self.adjustments = Table(
                'adjustments',# FOREIGN KEY - fk_adj_Trade_id
                Column('Trade_id',Column('adjusted_at',SMALLINT()),FLOAT(2)),DATE()),)
            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类的子类并在那里填写表的详细信息,构建小型数据库应用程序的更正确方法是什么像这样吗?

解决方法

发生主键问题是因为 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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。