3

我一直在讀那ActiveRecord的交易將自動纏保存和銷燬的行動。我的問題涉及以下情況。的Rails 3.2的ActiveRecord交易

我有一個庫存系統跟蹤的出貨量,並創建發貨時調整產品的庫存量。當我刪除一批貨物時,我將其編程爲將貨件數量加回到產品的庫存量。這種情況是專爲如果用戶弄髒貨物而設計的。然後刪除貨件將裝運的物品重新加入庫存。

我的問題是當product_shipments通過環是有必要提供Product.transaction塊或I可以省略此由於破壞方法被自動包裝在一個交易?像我這樣在一個事務中包裝整個循環可以嗎?如何最好地確保所有這些數據庫操作都會在出現故障時回滾?

def destroy 
    @shipment = Shipment.find(params[:id]) 
    @shipments = @shipment.product_shipments 
    Product.transaction do 
    @shipments.each do |s| 
     @difference = -(s.qty_shipped.to_i) 
     Product.update_inventory_quantities(@difference, s.product_id) 
    end 
    end 
    @shipment.destroy 
    respond_with @shipment, :location => shipments_url 
end 
+0

我想看看[ActiveRecord的回調(http://guides.rubyonrails.org/active_record_validations_callbacks.html#callbacks-overview)用於這種東西。 – Mischa

回答

2

爲了詳細說明米莎的建議,ActiveRecord的回調將讓你提高在幾個重要方面這一代碼。首先,它刪除了促使你寫這個問題的醜陋的事務塊。幾乎總是,如果你在Rails中看到類似的事務塊,你可能做錯了什麼。其次,它開始讓事情回到他們所關心的地方。這不僅使維護變得更容易,因爲事情處於直觀的位置,並且主要利用內置的Rails方法,但它也會使測試變得更加容易。

我懷疑有沒有這個應用測試。當你進行這種調整時,這可能是一個很好的藉口。它開始分解到一些簡單的規格可能會走很長的路,而你的應用程序的複雜性看起來可能會讓你從額外的想法中受益(尤其是因爲涉及到金錢)。

我打算對你的對象的關係做一些假設,並將它們留在下面的示例代碼之外,但我想象的是has_many:涉及貨物,產品和ProductShipments的through_type情況。

你的控制器,清理:

def destroy 
    # note: you may not need the additional scope of @shipment - depending on your view, you may be able to omit the @ 
    @shipment = Shipment.destroy(params[:id]) 
    respond_with @shipment, :location => shipments_url 
end 

您的貨物模型:

class Shipment < ActiveRecord::Base 

    before_destroy :restore_unshipped_inventory 

    def restore_unshipped_inventory 
    product_shipments.each(&:restore_unshipped_inventory) 
    end 

end 

您的加盟模式:

class ProductShipment < ActiveRecord::Base 

    def restore_unshipped_inventory 
    difference = -(s.qty_shipped.to_i) 
    Product.update_inventory_quantities(difference, product.id) 
    end 

end 

老實說,我甚至把它遠一點,並使ProductShipment中的restore_unshipped_inventory在產品實例上運行(product.update_inventory_quantities),即使所有實例方法都調用了你在那裏的類方法,只是爲了進一步隔離不相關的邏輯。

+0

「我懷疑這個應用程序沒有測試。」大聲笑。你怎麼猜? – ctilley79

0

你做了什麼是對的。但是您需要在交易中添加@ shipment.destroy。所以,如果交易失敗,我們的貨物將不會被銷燬。如果u事務由於某種原因失敗,則不會觸發提交,並且您的事務將回滾。