Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foreign key relationships missing when reflecting db in SqlAlchemy

I am attempting to use SqlAlchemy (0.5.8) to interface with a legacy database declaratively and using reflection. My test code looks like this:

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)

Now when I attempt to run a query using the join between these two tables (defined by a foreign key constraint in the underlying oracle schema):

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

... no joy:

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

However:

>>> 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)]

Which clearly indicates that the reflection picked up the foreign key.

Where am I going wrong?

like image 982
nroam Avatar asked Jul 21 '10 15:07

nroam


People also ask

What is reflection in SQLAlchemy?

A Table object can be instructed to load information about itself from the corresponding database schema object already existing within the database. This process is called reflection.

How will you implement many to many relationship in SQLAlchemy?

Many to Many relationship between two tables is achieved by adding an association table such that it has two foreign keys - one from each table's primary key.


1 Answers

After debugging the script + SqlAlchemy code with Eclipse, I found that the list of tables/columns is kept internally in lower case. As such, there was never any possibility of a match between EXPRESSION.foreignkey and expression.foreignkey. Hence the error message.

Digging deep into the SqlAlchemy documentation ( http://www.sqlalchemy.org/docs/reference/dialects/oracle.html#identifier-casing ) I then found the following:

"In Oracle, the data dictionary represents all case insensitive identifier names using UPPERCASE text. SQLAlchemy on the other hand considers an all-lower case identifier name to be case insensitive. The Oracle dialect converts all case insensitive identifiers to and from those two formats during schema level communication, such as reflection of tables and indexes. Using an UPPERCASE name on the SQLAlchemy side indicates a case sensitive identifier, and SQLAlchemy will quote the name - this will cause mismatches against data dictionary data received from Oracle, so unless identifier names have been truly created as case sensitive (i.e. using quoted names), all lowercase names should be used on the SQLAlchemy side."

So my code works if it looks like this (differences are case-changes only):

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)

... which spits out:

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

Case closed.

like image 75
nroam Avatar answered Oct 13 '22 22:10

nroam