SqlAlchemy反映数据库时缺失外键关系

5 投票
1 回答
3409 浏览
提问于 2025-04-16 01:38

我正在尝试使用SqlAlchemy(0.5.8)来以声明的方式与一个旧数据库进行交互,并使用反射功能。我的测试代码看起来是这样的:

from sqlalchemy import *
from sqlalchemy.orm import create_session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
engine = create_engine('oracle://schemaname:pwd@SID')
meta = MetaData(bind=engine)

class CONSTRUCT(Base):
    __table__ = Table('CONSTRUCT', meta, autoload=True)

class EXPRESSION(Base):
    __table__ = Table('EXPRESSION', meta, autoload=True)

session = create_session(bind=engine)

现在,当我尝试运行一个查询,想要通过这两个表之间的连接(这个连接是由底层Oracle数据库的外键约束定义的)时:

print session.query(EXPRESSION).join(PURIFICATION)

...却没有成功:

sqlalchemy.exc.ArgumentError: Can't find any foreign key relationships between 'EXPRESSION' and 'PURIFICATION'

但是:

>>> EXPRESSION.epiconstruct_pkey.property.columns 
[Column(u'epiconstruct_pkey', OracleNumeric(precision=10, scale=2, asdecimal=True,
length=None), ForeignKey(u'construct.pkey'), table=<EXPRESSION>, nullable=False)]

>>> CONSTRUCT.pkey.property.columns
[Column(u'pkey', OracleNumeric(precision=38, scale=0, asdecimal=True, length=None),
table=<CONSTRUCT>, primary_key=True, nullable=False)]

这明显表明反射功能已经识别到了外键。

我哪里出错了呢?

1 个回答

6

在用Eclipse调试脚本和SqlAlchemy代码时,我发现表和列的名称在内部都是小写的。因此,EXPRESSION.foreignkey和expression.foreignkey之间根本不可能匹配,这就是错误信息的原因。

我深入研究了SqlAlchemy的文档(http://www.sqlalchemy.org/docs/reference/dialects/oracle.html#identifier-casing),发现了以下内容:

“在Oracle中,数据字典中的所有标识符名称都是不区分大小写的,使用大写字母表示。而SqlAlchemy则认为全小写的标识符名称是不区分大小写的。Oracle的方言在进行模式级别的通信时,比如反射表和索引,会在这两种格式之间转换所有不区分大小写的标识符。在SqlAlchemy中使用大写名称表示这是一个区分大小写的标识符,SqlAlchemy会对这个名称进行引用,这样就会导致与从Oracle接收到的数据字典数据不匹配。因此,除非标识符名称确实是以区分大小写的方式创建的(也就是说使用了引用名称),否则在SqlAlchemy中应该使用全小写的名称。”

所以我的代码如果看起来像这样(只是大小写有变化):

from sqlalchemy import *
from sqlalchemy.orm import create_session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
engine = create_engine('oracle://EPIGENETICS:sgc04lab@ELN')
meta = MetaData(bind=engine)

class construct(Base):
    __table__ = Table('construct', meta, autoload=True)

class expression(Base):
    __table__ = Table('expression', meta, autoload=True)

class purification(Base):
    __table__ = Table('purification', meta, autoload=True)

session = create_session(bind=engine)
print session.query(expression).join(purification,expression)

... 结果是:

SELECT expression.pkey AS expression_pkey, expression.cellline AS expression_cellline, expression.epiconstruct_pkey AS expression_epiconstruct_pkey, expression.elnexp AS expression_elnexp, expression.expression_id AS expression_expression_id, expression.expressioncomments AS expression_expressioncomments, expression.cellmass AS expression_cellmass, expression.datestamp AS expression_datestamp, expression.person AS expression_person, expression.soluble AS expression_soluble, expression.semet AS expression_semet, expression.scale AS expression_scale, expression.purtest AS expression_purtest, expression.nmrlabelled AS expression_nmrlabelled, expression.yield AS expression_yield 
FROM expression JOIN purification ON expression.pkey = purification.epiexpression_pkey JOIN expression ON expression.pkey = purification.epiexpression_pkey

事情解决了。

撰写回答