SQLalchemy 找不到用于创建外键的 table
SQLalchemy not find table for creating foreign key
我在使用 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'"
元数据:
db = create_engine('postgresql+psycopg2:...//')
meta = MetaData()
meta.bind = db
人 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')
错误 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')
正常table(正常创建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')
创建调用:
meta.create_all(checkfirst=True)
完整的错误日志:
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'
通过将以下行添加到我的 parent
table 解决了我的问题。在声明的情况下:
children = relationship("Child")
否则:SQLAlchemy - Classic Mapper
也尝试在 here (SO) 中查看,可能会有帮助。
解决方案是用实际列替换字符串:
Column('person_id', Integer, ForeignKey(tbl_person.c.id), primary_key=True)
在声明式的情况下,我通过简单地导入 'could not be found'.
的 class 解决了这个问题
结论
这个异常是因为MetaData实例中没有parent table的记录,需要在DB中检索这个table .
调用class MetaData的“reflect”函数,获取数据库中所有存在的table。应该这样使用
def upgrade(migrate_engine):
meta.bind = migrate_engine
meta.reflect() # <------ Obtain all tables here.
aggregate_metadata.create()
aggregate_hosts.create()
描述
A table 与另一个 table 在不同的文件中,后者具有与之关联的外键。
这样的话,sqlalchemy会在create tables的时候找不到对应的table,如下图:
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'
例如:
# 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()
根本原因
我们定位到抛出异常的地方
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'
我们可以找到对应的代码进行调试:
# 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 )
然后我们可以得到以下异常:
Exception: imgrass->
- parenttable: aggregate_metadata
- parenttable.metadata: MetaData(bind=Engine(mysql+pymysql://imgrass:***@172.17.0.1/demo))
- tablekey: aggregates
- colname: id
因此,parenttable.metadata 是 class MetaData 的实例,而 tablekey 是一个 table 名字。
我们可以合理地猜测 table 聚合 应该包含在 class MetaData 的实例中。
考虑到这个table的定义在另一个文件中,而MetaData实例有DB(bind=xxx)的连接方式,所以[=中应该有一个函数66=] MetaData获取数据库中的所有table。
在元数据中,我们可以找到这个函数
# File: sqlalchemy/sql/schema.py
class MetaData(SchemaItem):
...
def reflect(...):
r"""Load all available table definitions from the database.
...
从它的描述中,我们可以猜到它的功能,让我们在我的脚本中应用它:
# 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()
没关系!
虽然投票最高的答案解决了问题,但用对象替换字符串会强制您按特定顺序定义表(对于非常大的数据库,可以是 non-trivial)。来自 SQLAlchemy docs:
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.
您可以通过将模式传递给 ForeignKey
来继续使用字符串。例如,而不是做:
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')
一个人可以做到:
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')
我在使用 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'"
元数据:
db = create_engine('postgresql+psycopg2:...//')
meta = MetaData()
meta.bind = db
人 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')
错误 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')
正常table(正常创建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')
创建调用:
meta.create_all(checkfirst=True)
完整的错误日志:
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'
通过将以下行添加到我的 parent
table 解决了我的问题。在声明的情况下:
children = relationship("Child")
否则:SQLAlchemy - Classic Mapper
也尝试在 here (SO) 中查看,可能会有帮助。
解决方案是用实际列替换字符串:
Column('person_id', Integer, ForeignKey(tbl_person.c.id), primary_key=True)
在声明式的情况下,我通过简单地导入 'could not be found'.
的 class 解决了这个问题结论
这个异常是因为MetaData实例中没有parent table的记录,需要在DB中检索这个table . 调用class MetaData的“reflect”函数,获取数据库中所有存在的table。应该这样使用
def upgrade(migrate_engine):
meta.bind = migrate_engine
meta.reflect() # <------ Obtain all tables here.
aggregate_metadata.create()
aggregate_hosts.create()
描述
A table 与另一个 table 在不同的文件中,后者具有与之关联的外键。 这样的话,sqlalchemy会在create tables的时候找不到对应的table,如下图:
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'
例如:
# 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()
根本原因
我们定位到抛出异常的地方
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'
我们可以找到对应的代码进行调试:
# 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 )
然后我们可以得到以下异常:
Exception: imgrass->
- parenttable: aggregate_metadata
- parenttable.metadata: MetaData(bind=Engine(mysql+pymysql://imgrass:***@172.17.0.1/demo))
- tablekey: aggregates
- colname: id
因此,parenttable.metadata 是 class MetaData 的实例,而 tablekey 是一个 table 名字。 我们可以合理地猜测 table 聚合 应该包含在 class MetaData 的实例中。 考虑到这个table的定义在另一个文件中,而MetaData实例有DB(bind=xxx)的连接方式,所以[=中应该有一个函数66=] MetaData获取数据库中的所有table。
在元数据中,我们可以找到这个函数
# File: sqlalchemy/sql/schema.py
class MetaData(SchemaItem):
...
def reflect(...):
r"""Load all available table definitions from the database.
...
从它的描述中,我们可以猜到它的功能,让我们在我的脚本中应用它:
# 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()
没关系!
虽然投票最高的答案解决了问题,但用对象替换字符串会强制您按特定顺序定义表(对于非常大的数据库,可以是 non-trivial)。来自 SQLAlchemy docs:
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.
您可以通过将模式传递给 ForeignKey
来继续使用字符串。例如,而不是做:
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')
一个人可以做到:
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')