![](/img/trans.png)
[英]If ActiveRecord::Base.transaction succeeded, do something
[英]where should ActiveRecord::Base.transaction be?
我有三種模型:清單,食物和數量。 列表和食物通過has_many:through通過數量關聯。 因此,每個數量具有三個參數:food_id,list_id和數量(整數)。
我的目標是每次創建列表時都創建一個新的數量(與該列表關聯)。 我希望使用事務來執行此操作,以便必須成功創建所有對象,否則將不會創建任何對象。
我的主要問題是:應在我的代碼中哪兒寫此事務? 我認為應該在List模型中,但是我不確定。 如果它應該在列表模型中,我不知道它在列表模型中的位置。 我認為它不應該位於List控制器中,我在Mark Daggett的博客的評論中找到了建議,建議它可以位於獨立的數據訪問對象中,但是我不確定該怎么做。
其次:交易本身。 很難說出我的錯誤是在交易中還是在其位置。
如果相關,在回答另一個問題后我會提出這個問題 ,但是我認為這應該是一個新問題,因為我沒有找到專門針對交易的類似問題。
我的列表模型,該事務當前所在的位置:
class List < ActiveRecord::Base
has_many :quantities
has_many :foods, :through => :quantities
before_save { self.name = name.downcase }
validates :days, presence: true, :numericality => { :greater_than => 0 }
validates :name, length: { maximum: 140 }, uniqueness: { case_sensitive: false }
end
ActiveRecord::Base.transaction do
@list = List.create
@a = Food.all.sample(1)
Quantity.create(food_id: @a, list_id: @list.id, amount: rand(6))
end
沒有錯誤,但是沒有創建新的Quantity,這使我認為除了至少一件事出錯之外,我還在做正確的事情。
我也試過
List.transaction do
代替
ActiveRecord::Base.transaction do
並得到相同的結果。
我想對這個問題的任何方向或暗示,都是由於我對一個非常基本的觀點的誤解造成的(非常基本,以至於我無法在文檔中找到關於它的任何信息)。 謝謝。
Rails 4.2.3,Cloud9。 開發數據庫= SQLite3,生產數據庫= postgres heroku。
您會收到錯誤,但看不到它們。 如果要查看它們,請致電Quantity.create! 而不是Quantity.create。 (或者您可以分配給一個臨時對象並查看它們,例如:q = Quantity.create(...); q.errors
該錯誤是由於您在類List中進行了兩次驗證(順便說一句壞名)而檢查了日期和名稱。
正如Ilan Berci所說,您可能沒有看到一些錯誤。 使用事務時,一種很好的做法是使用bang版本的方法來保存記錄,這樣,當任何驗證未通過時,您將得到一個異常,然后事務將被回滾。
關於第一個問題,有關在Rails項目中將復雜性放在何處的討論很多。 幾年前,座右銘是“瘦的控制器,胖模型”,這意味着應該提取模型的復雜性,使控制器更加難以測試,並且應用程序的流程必須明顯,可讀性更高。 我更喜歡“緊縮一切”的哲學:沒有絕對的規則,您可以將所有內容放置在任何地方,但是一旦變得有點復雜,請將其提取到只負責一件或很少一件事情的類中。 使內容小巧,簡單,經過測試,並在需要時進行整理。
就您而言,您可以創建服務或用例,並在控制器中簡單地使用它:
class CreateList
def create!
ActiveRecord::Base.transaction do
@list = List.create!
@food = Food.all.sample(1)
Quantity.create!(food: @food, list: @list, amount: rand(6))
end
end
end
如您所見,測試該類非常簡單! 大多數“正確方法”僅憑經驗顯示其真實價值,而且從來沒有獨特,正確的方法來做事,因此,請繼續嘗試不同的技術,直到您掌握解決問題的方法!
我終於使我的交易生效並開始提供服務了! 謝謝ilan和mrodrigues的建議。 我創建的新的Quantity邏輯超出了控制器和模型的范圍,我正在接近“所有東西都瘦”的代碼,並且我有很多需要保存的地方。
作為參考, 這篇關於John Nunemaker的Railstips的帖子幫助我以可由Lists控制器識別的方式(通過使用self)定義WriteList方法。
這是我編寫的用於在創建列表時處理數量創建的服務:
class WriteList
def self.write!(a)
ActiveRecord::Base.transaction do
a.save!
@a = Food.all.sample.id
Quantity.create!(food_id: @a, list_id: a.id, amount: rand(6))
end
end
end
這是控制器的創建部分:
def create
list = List.new(list_params)
if WriteList.write!(list)
flash[:success] = "A list has been created!"
redirect_to list
else
render 'new'
end
end
如果您覺得有任何其他提示,我將不勝感激。
我已經根據mrodrigues的反饋更新了代碼:
write
方法在失敗時返回false。 write
方法(在服務中)可以實例化create
方法(在控制器中)內的對象。 write
方法的代碼。 為了使所有這些都能正常工作,我在WriteList
類中添加了一個def initialize
方法,您可能都假定它在那里,但實際上沒有。 我希望這是明智的選擇(而不是胡扯)。
編輯
在正確的軌道上,我進行了以下更改:
結束編輯
現在,它可以正常工作。 對於長期的組織/結構/建設方面的進一步反饋,我表示贊賞。
app/services/write_list.rb
:
class WriteList
def initialize(params)
@params=params
end
def write
ActiveRecord::Base.transaction do
food = Food.all.sample.id
list = List.new(days: @params[:days], name: @params[:name])
list.save!
Quantity.create!(food_id: food, list_id: list[:id], amount: 1+rand(6))
return list
end
rescue
return List.new(days: @params[:days], name: @params[:name])
end
end
app\\controllers\\lists_controller
相關部分:
def create
@list = WriteList.new(list_params).write
if @list.save
flash[:success] = "A list has been created!"
redirect_to @list
else
render 'new'
end
end
順便說一句:我希望我發布與以前的答案/問題相比有所改善的答案的做法是適當的; 有人請告訴我是否不是。 我發現它對於跟蹤我的進度和收集更多反饋非常有幫助,希望其他人(尤其是像我這樣的初學者)可以從更新中有所收獲。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.