2013-03-01 27 views
0

我正在使用擴展ActiveRecord :: Base的幾個模型類來處理Rails應用程序。我已經在這些模型類中的一些模型類上實現了多種方法,可以用來更新數據庫內容,並且這些方法都是按照我希望它們工作的方式工作的。例如,我有一個TeamStats類存儲各種統計值到數據庫中,我已經定義了允許我通過運用博弈結果來更新這些屬性的方法:對ActiveRecord實例進行更改我從不想提交

class TeamStats < ActiveRecord::Base 

    belongs_to :team 

    def << game_result 
    # Lots of stuff here to update persisted attributes for 
    # wins, losses, total points scored, etc 
    end 

end 

class Team < ActiveRecord::Base 
    has_one :stats, :class_name => TeamStats, :dependent => destroy 
end 

現在我發現自己想要重用邏輯但是有一些我從不想提交給數據庫的臨時數據。例如,我想重新計算使用遊戲子集的團隊的統計數據,而不是所有的統計數據。所以,我有代碼,不會是這樣的:

# relevant_game_results is an array containing game results I want considered 
teams.each do |team| 
    new_stats = TeamStats.new(:team => team) 
    relevent_game_results.each do |results| 
     new_stats << results 
    end 
end 

# Do stuff to choose a team based on these un-persisted stats that have been 
# assigned to the team 

# After I'm done, through all the team_stats I created and make sure all of the 
# related team model objects still reference their original team stats values 

我給這家最初的計劃是修改我的模型實例,然後調用這些實例reload當我完成了。例如:

teams.each do |team| 
    team.reload 
end 

我認爲,將工作,但我不得不這樣做對大量的對象,如果我可以,我寧願做一個操作。

看來我真正需要的是一個總是回滾而不是承諾的事務。什麼是最合適的「軌道方式」來做到這一點?我應該在transaction區塊內做到這一點,然後在我的區塊末尾加註ActiveRecord::Rollback?換句話說,像這樣的東西?

Team.transaction do 

    teams.each do |team| 
     new_stats = TeamStats.new(:team => team) 
     relevent_game_results.each do |results| 
      new_stats << results 
     end 
    end 

    # Do stuff to choose a team based on these un-persisted stats that have been 
    # assigned to the team 

    raise ActiveRecord::Rollback 
end 

這似乎有點「髒」我但那只是我的Java背景透進來;) 有沒有一種更簡潔的方法就是更符合Rails的方式行?

更新:事實證明,將此包裝在事務中並回滾不僅是醜陋的,看起來很難使其正常工作。因爲執行此代碼的方法本身可以在另一個事務中,並且由於ActiveRecord關係對象的某些更改傾向被自動保存,所以我不得不跳過許多環節才能使其工作。

基於接受的答案中的建議,我已經完全創建了一個新的TeamStats對象,並且從不保存它。似乎對我更好。

+0

你能提供一個愚蠢的簡單的操作示例嗎? – 2013-03-01 02:51:51

回答

1

而不是回滾,爲什麼不復制,然後永遠不會保存它?例如:

# in TeamStats 

def hypothetically_add(games) 
    copy = dup 
    games.each {|game| copy << game } 
    copy 
end 

也許還追加遊戲的統計對象一次一個是不正確的方法,如果您正在生成基於查詢所有遊戲的比賽統計。操作遊戲的集合可能會更好。

# in Team 

def stats_from_games_where(*conditions) 
    games.where(*conditions).reduce(Hash.new(0)) do |stats, game| 
    stats[:wins] += 1 if # we won 
    ... 
    end 
end 
+0

我也考慮過製作副本......最後,我可能會被迫這樣做。我在製作副本時遇到的問題是,TeamStats副本會將'belongs_to'設爲同一個團隊,該團隊使用'has_one'來處理與其統計資料的關係。因此,當我製作統計數據副本時,相關團隊是否也會更新以指向新的(未保存的)統計數據?我也可以製作團隊副本,但是我會讓它更難以回到我需要的東西 - 參考根據其統計信息選擇的原始Team對象。 – 2013-03-02 02:48:48

+0

不要通過關聯。只需製作一個重複的TeamStats對象並手動分配team_id即可。我不認爲這會覆蓋它,但你必須檢查。 – AJcodez 2013-03-02 17:07:58

+0

事實證明,我完全不必擔心這種關聯。在考慮了你的建議並參考了文檔後,我發現它說:「將對象分配給belongs_to關聯不會保存對象,因爲外鍵字段屬於父對象,它也不會保存父對象。」所以我改變了我的代碼來創建一個副本,並且從來沒有保存過這個副本,並且一切都很順利。謝謝! – 2013-03-02 18:54:36

1

你應該看看像ActiveAttr(https://github.com/cgriego/active_attr),或Virtus https://github.com/solnic/virtus。你會創建完全在內存中的對象。還有一個很好的railscast:http://railscasts.com/episodes/326-activeattr

+0

我認爲這種方法存在的問題是我沒有嘗試在大多數時間使用內存中的對象。在大多數情況下,我真的希望將這些模型更改發送到數據庫。這只是在某些情況下,我想要做一些不承諾的操作。 – 2013-03-01 03:28:11

+0

爲什麼不把邏輯轉移到只負責計算統計信息的類中?然後它將負責根據需要更新TeamStats(或根據情況)。 – rainkinz 2013-03-01 04:25:39

+0

我一直在想這件事,但它看起來像很多額外的工作。所涉及的邏輯設置了自我的幾個不同屬性,所以如果我將該邏輯提取到它自己的類(或模塊)中,那麼我將不得不實現具有相同關係屬性的另一組類,這些屬性不會擴展ActiveRecord :: Base。我真的想要使用ActiveRecord給我的所有行爲,我只是不想更多地控制我的更改是否被保存。 – 2013-03-01 04:59:58

相關問題