2016-06-13 80 views
4

我是新來的SQLAlchemy(奧姆斯在通常情況下),我試圖在移動現有的應用程序SQLAlchemy的,這樣我們就可以從現有(和繁瑣的更新)查詢轉移一些代碼的複雜性,到Python。不幸的是,我在數據庫反射後立即出現錯誤。雖然我可以直接查詢表,但我實際上沒有直接訪問類或類之間的關係。下面是我正在嘗試做的一個大約最小的例子。SQLAlchemy的自動映射backref錯誤

現存的Postgres數據表:

dev=> \d+ gmt_file 
             Table "public.gmt_file" 
    Column |  Type  | Modifiers | Storage | Stats target | Description 
-----------+--------------+-----------+----------+--------------+------------- 
file_id | integer  | not null | plain |    | 
     a | integer  |   | plain |    | 
     b | integer  |   | plain |    | 
Indexes: 
    "gmt_file_pk" PRIMARY KEY, btree (file_id) 
Foreign-key constraints: 
    "gmt_file_a_fk" FOREIGN KEY (a) REFERENCES cmn_user(user_id) 
    "gmt_file_b_fk" FOREIGN KEY (b) REFERENCES cmn_user(user_id) 

SQLAlchemy的應用程序(小例子):

from sqlalchemy import create_engine 
from sqlalchemy.orm import Session,Mapper 
from sqlalchemy.ext.automap import automap_base 

engine = create_engine('postgresql://user:[email protected]:5432/dev') 
Base = automap_base() 
Base.prepare(engine, reflect=True) 
session = Session(engine,autocommit=True) 

session.query(Base.classes.gmt_file).all() 

從我目前可以告訴,這將引發因爲兩者ab具有一個backref錯誤與不同表中的相同字段的外鍵關係(這通常發生在現有數據庫中)。我嘗試了多種處理此錯誤的方法,包括創建自定義命名函數(name_for_scalar_relationship()name_for_collection_relationship()),但無濟於事。有沒有一種標準的方法來處理這個問題,或者在SQLAlchemy中禁用反射過程中的backref創建?

的最終目標將是反映以自動化的方式分貝,而不必編寫自定義名稱爲數百個目前存在表的映射,但是我在適合做什麼損失。任何幫助表示讚賞。

謝謝

+0

我不確定這是否有助於找到解決方案,但我可以直接使用'__table__'子對象查詢表。例如。 'session.query(Base.classes.gmt_file .__ table __)。all()'不會引發錯誤。有什麼想法嗎? – DavidWayne

回答

1

看起來我們得使用自動映射,當一個以上的外鍵是指在同一列的屬性名稱衝突。

Base.prepare允許參數name_for_scalar_relationshipname_for_collection_relationship,它們採用用於生成屬性名稱的函數。 (請參閱AutomapBase.prepare()name_for_collection_relationship()的文檔)我能夠通過定義自己的函數來解決backref錯誤。

修改你的小例子:

from sqlalchemy import create_engine 
from sqlalchemy.orm import Session,Mapper 
from sqlalchemy.ext.automap import automap_base, name_for_collection_relationship 

engine = create_engine('postgresql://user:[email protected]:5432/dev') 
Base = automap_base() 

def _name_for_collection_relationship(base, local_cls, referred_cls, constraint): 
    if constraint.name: 
     return constraint.name.lower() 
    # if this didn't work, revert to the default behavior 
    return name_for_collection_relationship(base, local_cls, referred_cls, constraint) 

Base.prepare(engine, reflect=True, name_for_collection_relationship=_name_for_collection_relationship) 
session = Session(engine,autocommit=True) 

session.query(Base.classes.gmt_file).all() 

這樣應該可以讓你與屬性名稱gmt_file_a_fkgmt_file_b_fk你的類。

此方法爲我工作。如果它不起作用,您也可以嘗試類似地重新定義name_for_scalar_relationship()

如果你想在所有覆蓋類,你必須確保正確定義的列和關係。例如:

from sqlalchemy import Column, Integer, ForeignKey 
from sqlalchemy.orm import relationship 

class GmtFile(Base): 
    __tablename___ = 'gmt_file' 

    file_id = Column('file_id', Integer, primary_key=True) 

    a = Column('a', Integer, ForeignKey('CmnUser.user_id', name='gmt_file_a')) 
    b = Column('b', Integer, ForeignKey('CmnUser.user_id', name='gmt_file_b')) 
    # you'll need to define the class CmnUser as well 

    # these variable names need to be the same as the ForeignKey names above 
    gmt_file_a = relationship('CmnUser', foreign_keys=[a]) 
    gmt_file_b = relationship('CmnUser', foreign_keys=[b]) 
+0

它適用於我。 謝謝你,當我需要它的時候,你就回答了:)。 – Frane

+0

我已經從SQLAlchemy轉移過來,所以我沒有驗證過這將適用於我的情況。但我認爲它是正確的,因爲它看起來可以起作用,它至少可以解決另一個人的問題。 – DavidWayne