2015-04-12 20 views
4

我正在使用SQL鍊金術並且有一些特定於模式的模式。模式的名稱是使用帳戶ID派生的,所以在我打開我的應用程序服務或存儲庫層之前,我沒有該模式的名稱。我想知道是否可以針對運行時動態設置模式的實體運行查詢?在運行時在實體上的sqlalchemy動態模式

我知道我需要設置__table_args__['schema']並試圖這樣做,使用內置的()的類型,但我總是得到以下錯誤:

could not assemble any primary key columns for mapped table 

我準備放棄,只是寫直sql,但我真的很討厭這樣做。任何想法如何做到這一點?我使用SA 0.99,我確實有一個PK映射。

謝謝

+0

您能否提供一個有關導致此錯誤和堆棧跟蹤的代碼的工作示例? –

+1

我很感謝這裏的幫助,但我現在不得不經過一個動態模式的想法,只是有一個應用程序的視圖。我花了太多的時間試圖做到這一點,雖然我沒有使用SA成功,但我還有其他問題。 –

回答

3

一個選項是反映特定的賬戶相關表。這是關於此事的SqlAlchemy Documentation

或者,您可以使用靜態schema屬性創建表並根據需要在運行時更新它並運行所需的查詢。我想不出一種非雜亂的方式來做到這一點。因此,這裏是凌亂的選項

無論何時切換帳戶,都使用循環更新每個表定義中的模式屬性。

  1. 添加帳戶特定於列表的所有表。

    • 如果表在聲明語法表達,那麼你就必須修改DeclarativeName.__table__.schema屬性。我不確定你是否需要修改DeclarativeName.__table_args__['schema'],但我想它不會受到傷害。
    • 如果表格以舊式表格語法表示,則必須修改Table.schema屬性。
  2. 如果您使用文本任何關係或外鍵,那麼將打破,你必須檢查是否有這樣的硬編碼使用每個表並改變它們
    例如

    • user_id = Column(ForeignKey('my_schema.user.id'))需要寫成user_id = Column(ForeignKey(User.id))。然後,您可以將User的架構更改爲my_new_schema。否則,在查詢時間sqlalchemy將被混淆,因爲外鍵將指向my_schema.user.id,而查詢將指向my_new_schema.user
  3. 我不確定是否可以在不使用純文本的情況下表達更復雜的關係,所以我想這是我提出的解決方案的限制。

這是我在終端寫了一個例子:

>>> from sqlalchemy import Column, Table, Integer, String, select, ForeignKey 
>>> from sqlalchemy.orm import relationship, backref 
>>> from sqlalchemy.ext.declarative import declarative_base 
>>> B = declarative_base() 
>>> 
>>> class User(B): 
... __tablename__ = 'user' 
... __table_args__ = {'schema': 'first_schema'} 
... id = Column(Integer, primary_key=True) 
... name = Column(String) 
... email = Column(String) 
... 
>>> class Posts(B): 
... __tablename__ = 'posts' 
... __table_args__ = {'schema':'first_schema'} 
... id = Column(Integer, primary_key=True) 
... user_id = Column(ForeignKey(User.id)) 
... text = Column(String) 
... 
>>> str(select([User.id, Posts.text]).select_from(User.__table__.join(Posts))) 
'SELECT first_schema."user".id, first_schema.posts.text \nFROM first_schema."user" JOIN first_schema.posts ON first_schema."user".id = first_schema.posts.user_id' 
>>> account_specific = [User, Posts] 
>>> for Tbl in account_specific: 
... Tbl.__table__.schema = 'second_schema' 
... 
>>> str(select([User.id, Posts.text]).select_from(User.__table__.join(Posts))) 
'SELECT second_schema."user".id, second_schema.posts.text \nFROM second_schema."user" JOIN second_schema.posts ON second_schema."user".id = second_schema.posts.user_id' 

正如你看到相同的查詢是指second_schema後,我更新的表的模式屬性。

+0

這不是一個壞的解決方案,但我想知道範圍可能成爲一個問題。這是一個將在uWSGI或gunicorn中運行的Web應用程序。似乎可能改變類的值而不是實例可能最終會從錯誤的模式中給用戶數據。這可能只是我偏執狂。 –

+0

更簡單的解決方案是將每個帳戶的表格聲明覆制到新文件中,然後在需要時從該文件導入。 –

+0

您在「將...複製到新文件中」時丟失了我。這對於可伸縮的動態應用程序來說不是一個可行的解決方案。我認爲這是不值得的。 –

1

它們是靜態設置的。外鍵需要同樣的treatement,我有另外一個問題,因爲我有一個包含多個表,所以我這樣做多種模式:

from sqlalchemy.ext.declarative import declarative_base 

staging_dbase = declarative_base() 
model_dbase = declarative_base() 


def adjust_schemas(staging, model): 
    for vv in staging_dbase.metadata.tables.values(): 
     vv.schema = staging 
    for vv in model_dbase.metadata.tables.values(): 
     vv.schema = model 


def all_tables(): 
    return staging_dbase.metadata.tables.union(model_dbase.metadata.tables) 
在我的啓動代碼

然後:

adjust_schemas(staging=staging_name, model=model_name) 

你可以爲單個聲明性基礎進行修改。