2009-12-11 225 views
0

假設您有「lineitems」,並且您曾經從line_item定義「make」。在Rails遷移中遷移數據

最終你意識到make應該可以在自己的模型上,所以你創建了一個Make模型。

然後,您想要從line_items表中移除make列,但是要爲每個line_item移除您想要find_or_create_by(line_item.make)的make。

我如何有效地在軌道遷移中做到這一點?我敢肯定,我可以爲每個line_item運行一些簡單的find_or_create_by,但我擔心後備支持,所以我只是在這裏發佈此任何提示/建議/正確的方向。

謝謝!

回答

3

我想你應該檢查Make.count是否等於在刪除列之前的lineitems中的唯一總數,如果不是,則會引發錯誤。由於遷移是事務性的,如果它發生變化,模式不會改變,遷移也不會被標記爲已執行。因此,您可以這樣做:

class CreateMakesAndMigrateFromLineItems < ActiveRecord::Migration 
    def self.up 
    create_table :makes do |t| 
     t.string :name 
     … 
     t.timestamps 
    end 

    makes = LineItem.all.collect(:&make).uniq 
    makes.each { |make| Make.find_or_create_by_name make } 
    Make.count == makes.length ? remove_column(:line_items, :make) : raise "Boom!" 

    end 

    def self.down 
    # You'll want to put logic here to take you back to how things were before. Just in case! 
    drop_table :makes 
    add_column :line_items, :make 
    end 
end 
+0

事務性遷移僅在數據庫供應商支持時纔可用。 MySQL例如不提供事務性DDL,因此raise語句會導致遷移失敗並使數據庫處於不一致狀態。 – Cluster 2013-03-06 10:39:35

+0

如果你添加了什麼集羣說,也許分成兩個遷移,添加一個新的列,並做你需要做的任何事情。第二,當塵土已經沉澱,然後刪除任何不再需要的列。這不是完美的,但會給你一個回頭路。 – Ghoti 2013-07-31 09:23:23

0

您可以在遷移過程中放置​​常規ruby代碼,以便您可以創建新表,在舊模型中運行一些代碼將數據移動到新模型中,然後從原始模型中刪除列。這甚至是可逆的,所以你的遷移仍然可以在兩個方向上工作。

因此,針對您的情況,創建Make表並將make_id添加到lineitem。然後爲每個訂單項find_or_create添加lineitem上的make列,將返回的ID設置爲lineitem上的新make_id。完成後,從lineitem表中刪除舊的make列。