简体   繁体   English

SQLalchemy 找不到用于创建外键的表

[英]SQLalchemy not find table for creating foreign key

I have a problem with SQL Alchemy, while trying to create a database, i get:我在使用 SQL Alchemy 时遇到问题,在尝试创建数据库时,我得到:

"sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'estate_agent.person_id' could not find table 'person' with which to generate a foreign key to target column 'id'"

Meta datas:元数据:

db = create_engine('postgresql+psycopg2:...//')
meta = MetaData()
meta.bind = db

Person table:人员表:

tbl_person = Table(
   'person', meta,
   Column('id', Integer, Sequence('seq_person_id'), primary_key=True),
   Column('name', String(100), unique=True, nullable = False),
   Column('password', String(40), nullable = False),
   Column('person_type_id', Integer, ForeignKey("person_type.id"), nullable = False),
   Column('register_date', DateTime, default = datetime.now),
   Column('pendencies', String(200)),
   Column('active', Boolean, default = True),
   schema = 'public')

Bug Table:错误表:

tbl_estate_agent = Table(
   'estate_agent', meta,
   Column('person_id', Integer, ForeignKey("person.id"), primary_key = True),
   Column('prize_range_id', Integer, ForeignKey("prize_range.id"), nullable = False),
   schema = 'public')

Normal table (creating normally the fk)正常表(正常创建 fk)

tbl_person_agent = Table(
   'person_agent', meta,
   Column('person_id', Integer, ForeignKey("person.id"), primary_key = True),
   Column('prize_range_id', Integer, ForeignKey("prize_range.id"), nullable = False),
   schema = 'public')

Creation Call:创作召唤:

meta.create_all(checkfirst=True)

Complete error log:完整的错误日志:

Traceback (most recent call last):
   File "database_client.py", line 159, in <module>
    meta.create_all(checkfirst=True)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/schema.py", line 3404, in create_all
    tables=tables)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1616, in _run_visitor
    conn._run_visitor(visitorcallable, element, **kwargs)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1245, in _run_visitor
    **kwargs).traverse_single(element)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/visitors.py", line 120, in traverse_single
    return meth(obj, **kw)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/ddl.py", line 699, in visit_metadata
    collection = [t for t in sort_tables(tables)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/ddl.py", line 862, in sort_tables
    {'foreign_key': visit_foreign_key})
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/visitors.py", line 256, in traverse
    return traverse_using(iterate(obj, opts), obj, visitors)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/visitors.py", line 247, in traverse_using
    meth(target)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/ddl.py", line 853, in visit_foreign_key
    parent_table = fkey.column.table   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py", line 725, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
   File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/schema.py", line 1720, in column tablekey)
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'estate_agent.person_id' could not find table 'person' with which to generate a foreign key to target column 'id'

解决方案是用实际列替换字符串:

Column('person_id', Integer, ForeignKey(tbl_person.c.id), primary_key=True)

By adding the following line to my parent table solved my problem.通过将以下行添加到我的parent表解决了我的问题。 In case of Declarative:如果是声明式:

children = relationship("Child")

Otherwise: SQLAlchemy - Classic Mapper否则: SQLAlchemy - 经典映射器

Also try to have a look in here (SO) too, might help.也尝试看看这里(SO) ,可能会有所帮助。

在声明性的情况下,我通过简单地导入“无法找到”的类来解决这个问题。

Conclusion结论

This exception was caused because that there is no record of parent table in MetaData instance, and this table needs to be retrieved in the DB.这个异常是因为MetaData实例中没有父表的记录,需要在DB中检索该表。 Invoke the function "reflect" of class MetaData to obtain all existed tables on the DB.调用MetaData类的“reflect”函数来获取 DB 上所有存在的表。 It should be used like this它应该像这样使用

def upgrade(migrate_engine):

    meta.bind = migrate_engine

    meta.reflect() # <------ Obtain all tables here.

    aggregate_metadata.create()
    aggregate_hosts.create()

Description描述

A table is in a different file from another table that has a foreign key associate with it.一个表与另一个有外键关联的表位于不同的文件中。 In this case, sqlalchemy will fail to find the corresponding table when create tables, as shown below:在这种情况下, sqlalchemy在创建表时会找不到对应的表,如下图:

sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'aggregate_metadata.aggregate_id' could not find table 'aggregates' with which to generate a foreign key to target column 'id' sqlalchemy.exc.NoReferencedTableError:与列“aggregate_metadata.aggregate_id”关联的外键找不到表“聚合”,用于生成目标列“id”的外键

For example:例如:

# File 002_Add_aggregates_table.py
# ========================================
...

meta = MetaData()

aggregates = Table('aggregates', meta,
    ...
    Column('id', Integer, primary_key=True, nullable=False),
    mysql_engine='InnoDB',
    mysql_charset='utf8'
)

def upgrade(migrate_engine):
    meta.bind = migrate_engine
    aggregates.create()
# File 003_Add_aggregate_metadata_hosts.py
# ========================================
...

meta = MetaData()

aggregate_metadata = Table('aggregate_metadata', meta,
    ...
    Column('aggregate_id', Integer, ForeignKey('aggregates.id'), # <------ ForeignKey
           nullable=False),
    mysql_engine='InnoDB',
    mysql_charset='utf8'
    )

def upgrade(migrate_engine):

    meta.bind = migrate_engine
    aggregate_metadata.create()

Root cause根本原因

Let's locate at the point that throw the exception让我们定位在抛出异常的点

  File "/opt/xxx/.local/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 1113, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/opt/xxx/.local/lib/python3.6/site-packages/sqlalchemy/sql/schema.py", line 2394, in column
    tablekey,
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'aggregate_metadata.aggregate_id' could not find table 'aggregates' with which to generate a foreign key to target column 'id'

We could find the corresponding code and debug it:我们可以找到相应的代码并进行调试:

# File: sqlalchemy/sql/schema.py
2358     def column(self):
            ...
2371
2372        if isinstance(self._colspec, util.string_types):
2373
2374           parenttable, tablekey, colname = self._resolve_col_tokens()

# =========> BeginDebug
2375           raise Exception(
2376               'imgrass->\n'
2377               '  - parenttable: %s\n'
2378               '  - parenttable.metadata: %s\n'
2379               '  - tablekey: %s\n'
2380               '  - colname: %s' % (
2381                   parenttable,
2382                   parenttable.metadata,
2383                   tablekey,
2384                   colname
2385                   )
2386               )
# =========> EndDebug

2387
2388           if tablekey not in parenttable.metadata:
2389              raise exc.NoReferencedTableError(
2390                 "Foreign key associated with column '%s' could not find "
2391                 "table '%s' with which to generate a "
2392                 "foreign key to target column '%s'"
2393                 % (self.parent, tablekey, colname),
2394                 tablekey,
2395              )

Then we could get below exceptions:然后我们可以得到以下异常:

Exception: imgrass->
  - parenttable: aggregate_metadata
  - parenttable.metadata: MetaData(bind=Engine(mysql+pymysql://imgrass:***@172.17.0.1/demo))
  - tablekey: aggregates
  - colname: id

So, the parenttable.metadata is an instance of class MetaData , and the tablekey is a table name.因此, parenttable.metadata是类MetaData的实例, tablekey是表名。 We could reasonably guess that the table aggregates should be included in the instance of class MetaData .我们可以合理地猜测表聚合应该包含在类MetaData的实例中。 Considering that the definition of this table is in another file, and the MetaData instance has the connection way of DB(bind=xxx), there should be a function in class MetaData to obtain all tables in the DB.考虑到这张表的定义在另一个文件中,并且MetaData实例有DB(bind=xxx)的连接方式,所以MetaData类中应该有一个函数来获取DB中的所有表。

In MetaData, we could find this function在 MetaData 中,我们可以找到这个函数

# File: sqlalchemy/sql/schema.py

class MetaData(SchemaItem):
    ...

    def reflect(...):
        r"""Load all available table definitions from the database.
        ...

From its description, we could guess its function, let's apply it in my script:从它的描述中,我们可以猜测它的功能,让我们将它应用到我的脚本中:

# File 003_Add_aggregate_metadata_hosts.py
# ========================================
...

def upgrade(migrate_engine):

    meta.bind = migrate_engine

# ==================> BeginUpdate
    meta.reflect()
# ==================> EndUpdate

    aggregate_metadata.create()
    aggregate_hosts.create()

It's okay!没关系!

Although the top voted answer solves the issue, replacing strings with objects forces you to define tables in a specific order (which can be non-trivial for very large databases).尽管投票最多的答案解决了这个问题,但用对象替换字符串会迫使您以特定顺序定义表(这对于非常大的数据库来说可能很重要)。 From the SQLAlchemy docs :SQLAlchemy 文档

The advantage to using a string is that the in-python linkage between [different tables] is resolved only when first needed, so that table objects can be easily spread across multiple modules and defined in any order.使用字符串的好处是 [不同表] 之间的 in-python 链接仅在第一次需要时才被解析,因此表对象可以轻松地分布在多个模块中并以任何顺序定义。

You can continue to use strings by passing the schema to the ForeignKey .您可以通过将架构传递给ForeignKey继续使用字符串。 For example, instead of doing:例如,不要这样做:

tbl_estate_agent = Table(
   'estate_agent', meta,
   Column('person_id', Integer, ForeignKey("person.id"), primary_key = True),
   Column('prize_range_id', Integer, ForeignKey("prize_range.id"), nullable = False),
   schema = 'public')

One can do:一个可以做到:

tbl_estate_agent = Table(
   'estate_agent', meta,
   Column('person_id', Integer, ForeignKey("public.person.id"), primary_key = True),
   Column('prize_range_id', Integer, ForeignKey("public.prize_range.id"), nullable = False),
   schema = 'public')

Import the actual classes like below: 导入如下的实际类:

from sqlalchemy import Integer, ForeignKey, String, Column

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import relationship

and to suppress SQLAlchemy warnings use the following line in the configuration: 并禁止SQLAlchemy警告使用配置中的以下行:

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config ['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

Apart from this where is the 'price_range' table? 除此之外,'price_range'表在哪里? Add price_range table before adding ForeignKey. 在添加ForeignKey之前添加price_range表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM