2011-10-06 32 views
1

我試圖在現有數據庫上設置一對多關係。一對多關係使用sqlalchemy中的反射和聲明語法定義連接條件錯誤

簡化DDL是:

create table accnt (
    code varchar(20) not null 
    , def varchar(100) 
    , constraint pk_accnt primary key (code) 
); 

commit; 

create table slorder (
    code varchar(20) not null 
    , def varchar(100) 
    , dt date 
    , c_accnt varchar(20) not null 
    , constraint pk_slorder primary key (code) 
    , constraint fk_slorder_accnt foreign key (c_accnt) 
    references accnt (code) 
    on update cascade on delete cascade 
); 

commit; 

SQLAlchemy的代碼:

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

engine = create_engine('firebird://sysdba:[email protected]/d:\\prj\\db2\\makki.fdb?charset=WIN1254', echo=False) 
Base = declarative_base() 
Base.metadata.bind = engine 

class Accnt(Base): 
    __tablename__ = 'accnt' 
    __table_args__ = {'autoload': True} 

    defi = Column('def', String(100)) 

class SlOrder(Base): 
    __tablename__ = 'slorder' 
    __table_args__ = {'autoload': True} 

    defi = Column("def", String(100)) 
    accnt = relationship('Accnt', backref='slorders') 

sqlalchemy.exc.ArgumentError: Could not determine join condition between parent/child tables on relationship SlOrder.accnt. Specify a 'primaryjoin' expression. If 'secondary' is present, 'secondaryjoin' is needed as well. 

錯誤。

我對這個問題的可能解決方案是:

class SlOrder(Base): 
    __tablename__ = 'slorder' 
    __table_args__ = {'autoload': True} 

    defi = Column("def", String(100)) 
    c_accnt = Column("c_accnt", String(20), ForeignKey('accnt.code')) 

    accnt = relationship('Accnt', backref='slorders') 

但這種方法需要,我必須手動添加每一個外鍵約束列,這會導致使反射useles。 (因爲我有很多列的引用其他表。)

class SlOrder(Base): 
    __table__ = Table('accnt', metadata, autoload = True, autoload_with=engine) 
    accnt = relationship('Accnt', backref='slorders', primaryjoin=(__table__.c_accnt==Accnt.code)) 

這種方法有一個另一個後果(請參閱my previous question

所以我缺少什麼?使用反射和聲明式語法來定義關係的最佳方式是什麼?

編輯:

我已經想通SQLAlchemy的發現和建立關係,如果孩子表只有一個引用父表。

但如果孩子表中有多於一個參考:

create table slorder (
    code varchar(20) not null 
    , def varchar(100) 
    , dt date 
    , c_accnt varchar(20) not null 
    , c_accnt_ref varchar(20) 
    , constraint pk_slorder primary key (code) 
    , constraint fk_slorder_accnt foreign key (c_accnt) 
    references accnt (code) 
    on update cascade on delete cascade 

    , constraint fk_slorder_accnt_ref foreign key (c_accnt_ref) 
    references accnt (code) 
    on update cascade on delete no action 
); 

發生上述錯誤。

因此,如果兩個表之間存在多個關係,那麼SqlAlchemy的預期行爲會產生錯誤?

回答

1

我認爲你的代碼應該自動反映ForeignKey並且沒有任何改變地用於關係。
只是一些想法,但探索的問題:

  1. 確保您不具有的多重ForeignKey到同父表,否則,您必須指定使用primaryjoin參數的連接條件,因爲SA不能自動決定哪些一個使用。
  2. 確保你確實有在slorder表中定義的ForeignKey(如圖中的代碼示例)
  3. 檢查也許有一些表定義的非默認schema,也許你需要定義一個在你的表_table_args__ = {'schema': 'my_schema'}(只是猜測,因爲我不知道firebird真的沒有關於模式支持的想法)
  4. 檢查/調試反射步驟:sqlalchemy/dialects/firebird/base.pyget_foreign_keys。檢查SQL語句fkqry並直接在數據庫上執行它,看它是否反映了你的ForeignKey。如果沒有,請嘗試找出原因。
+0

1.是的,我確實有 2.火鳥沒有架構支持 3. SQLAlchemy的正確發現我的外鍵和它的屬性。 – ctengiz

+0

嗯,你真的回答你的問題。不幸的是你原來的(簡化)代碼沒有公開這個問題,這是多個FK到父表的確實存在。爲了完整性,我會更新我的答案。 – van

+0

...我很驚訝,儘管你的解決方案'1.'工作雖然... – van

2

我想你必須在子表中添加ForeignKey

通過定義ForeignKey的方式,您可以將值分配給c_accnt,也可以將父對象分配給accnt

內部sqlalchemy激發您在primaryjoin中編寫的查詢。如果沒有外鍵,那麼模型無法理解它在哪個字段上運行查詢。

您可以同時使用這兩種方法。但我個人更喜歡ForeignKeyrelationForeignKey。這樣你就必須編寫更多的代碼,但它可以靈活地直接賦值和對象。

相關問題