3

私は、ActiveRecord トランザクションが保存および破棄アクションを自動的にラップしていることを読んでいます。私の質問は、次のシナリオに関連しています。

出荷を追跡し、出荷が作成されたときに製品の在庫量を調整する在庫システムがあります。出荷を削除すると、出荷数量が製品の在庫量に追加されるようにプログラムされています。このシナリオは、ユーザーが貨物を汚した場合のために設計されています。出荷を削除すると、出荷されたアイテムが在庫に追加されます。

私の質問は、product_shipments がループされるとき、Product.transaction ブロックを提供する必要があるか、または destroy メソッドが自動的にトランザクションにラップされるため、これを省略できますか? 私のようにループ全体をトランザクションでラップしても大丈夫ですか? 何か問題が発生した場合に、これらすべてのデータベース操作がロールバックされるようにするにはどうすればよいでしょうか?

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
4

2 に答える 2

2

Mischa の提案を詳しく説明すると、ActiveRecord コールバックを使用すると、いくつかの重要な方法でこのコードを改善できます。まず、この質問を書くように促した醜いトランザクション ブロックを削除します。ほとんどの場合、Rails でこのようなトランザクション ブロックが表示される場合は、おそらく何か間違ったことをしている可能性があります。第二に、物事を彼らの懸念がある場所に戻し始めます。これにより、物事が直感的な場所にあり、組み込みのRailsメソッドを大部分活用しているため、メンテナンスが容易になるだけでなく、テストも大幅に容易になります。

このアプリケーションにはテストがないと思われます。この調整を行っているので、これはいくつかを書く良い言い訳になるかもしれません。いくつかの単純な仕様が大いに役立つところまで崩壊し始めており、アプリケーションの複雑さは、追加された心の部分から利益を得ることができるように見えます (特にお金が関係するため)。

オブジェクトの関係について特定の仮定を行い、以下のコード例から除外しますが、出荷、製品、および製品出荷が含まれる has_many :through タイプの状況を想像していました。

コントローラー、クリーンアップ:

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 ( product.update_inventory_quantities) のインスタンスで動作するようにすることもできます。たとえそのインスタンス メソッドがそこにあるクラス メソッドを呼び出すことだけだったとしても、関連のないロジックをさらに分離するためです。

于 2012-10-09T05:42:09.037 に答える
0

あなたがしたことは正しいです。ただし、トランザクションに@shipment.destroyを追加する必要があります。そのため、トランザクションが失敗した場合、あなたの出荷は破棄されません. 何らかの理由でトランザクションが失敗した場合、コミットは発生せず、トランザクションはロールバックされます。

于 2012-10-09T03:59:52.853 に答える