1

我有一個傳統的數據庫(甲骨文),在那個數據庫中我有幾個表持有不同的數據,但結構相同。我不允許以任何方式更改數據庫模式!如何動態設置模型的table_name?

我想要一個DRY ActiveRecord模型從右表中獲取正確的數據。問題是我需要動態覆蓋self.table_name才能使其工作。

這裏是我的代碼:

ActiveRecord的:將所有類似的表繼承基類

class ListenLoc < ActiveRecord::Base 
    @@table_name = nil 

    def self.table_name 
    @@table_name 
    end  

    default_scope { where(validated: 1).where("URL_OK >= 0") } 
    scope :random_order, -> { order('DBMS_RANDOM.VALUE') } 
    scope :unvalidated, -> { unscope(:where).where(validated: 0) } 


    def self.get_category(cat_id) 
    where("cat_id = ?", cat_id) 
    end  

    def self.rand_sample(cat_id, lim) 
    where("cat_id = ?", cat_id).random_order.limit(lim) 
    end  
end 

子類看起來像這樣:

一個

class ListenLocA < ListenLoc 
    @@table_name = 'LISTEN_ADDR_A' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatA', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 

B.

class ListenLocB < ListenLoc 
    @@table_name = 'LISTEN_ADDR_B' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 

上述的工作,但我已經注意到,做特定的選擇查找時有一些陷阱。

這是一個很好的方法嗎?有沒有更好的方式動態傳遞self.table_name

更新:

有人會認爲,這應該工作,但我得到的表不存在由於ActiveRecord的嘗試創建一個對象之前驗證表,self.table_name未設置錯誤在ListenLoc模型上動態。

class ListenLocB < ListenLoc 
    self.table_name = 'LISTEN_ADDR_B' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 
+1

就個人而言,我會爲您的案例中的每個表有不同的模型。您可以在'ListenLoc'中將相似的代碼解壓縮成 –

回答

2

我意識到什麼是,我可以只使用superclass不使用全局變量,這是我結束了使用。與全局變量不同,這不會造成競爭條件問題。

class ListenLocB < ListenLoc 
    superclass.table_name = 'LISTEN_ADDR_B' # or ListenLoc.table_name 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 
1

在Ruby類中,變量在整個層次結構中共享,因此您的方法不起作用。類變量背後的一般想法 - 不要使用它,除非你100%確定你知道你在做什麼。甚至當你是 - 最有可能更好的方法。

至於實際問題 - 你用table_name完成的操作不是乾涸的,因爲你的增加了比你保存的更多的行。而且,它使閱讀變得更加困難。

只要把

self.table_name = 

,它應該是在每一個模型 - 這將是簡明易讀。

另一種選擇是使用本地化的常量,而不是被綁定到ListenLoc類:

class ListenLoc 
    def self.table_name 
    TABLE_NAME 
    end 
end 

class ListenLocB < ListenLoc 
    ::TABLE_NAME = 'LISTEN_ADDR_B' 
end 

爲什麼這個工程?

我的理解是這樣的:

通過寫::TABLE_NAME您在全局範圍內定義常量TABLE_NAME

當您的電話傳播到ListenDoc類時,它會嘗試解析常量ListenDoc::TABLE_NAME,並且它沒有找到它的範圍。然後它看起來是否在外部範圍中定義了常量TABLE_NAME,並且它發現::TABLE_NAME確實定義了,並且它的值是'LISTEN_ADDR_B'。因此,工作。

我可能已經不清楚,因爲我對題目的理解仍然漂浮,但它肯定是涉及到如何紅寶石搜索常量。

這不是很簡單的,因爲存在幾個注意事項(如所有的,畢竟)。

+0

如何添加比我保存的行更多的行? listenLoc類僅提取了一些方法,否則這些方法將駐留在其他模型中。 – mahatmanich

+0

@mahatmanich我只是指'table_name'/class變量部分,其他部分實際上是很好的(儘管我可能還有對繼承的關注,正如j-dexx在評論中所暗示的那樣,但這取決於你)。 –

+0

'self.table_name ='在實際模型中不起作用,那就是問題所在。它不會傳播到ListenLoc,即使它應該!所以上面的解決方案是我選擇的。 – mahatmanich