RAILS 3 - 控制器中的事务

use*_*684 9 controller transactions ruby-on-rails-3

我在Controller中有一个示例Action.

def some_action
 product = Product.new
 product.name = "namepro"
  if product.save
   client.update_attribute(:product_id,product.id)
  end
end
Run Code Online (Sandbox Code Playgroud)

如何为此代码添加事务?我尝试使用此示例代码:

def some_action
 **transaction do**
  product = Product.new
  product.name = "namepro"
   if product.save
    client.update_attribute(:product_create,Time.now)
   end
 **end**
end
Run Code Online (Sandbox Code Playgroud)

但它产生了这个错误:

undefined method `transaction'
Run Code Online (Sandbox Code Playgroud)

我读到在控制器中使用事务是一种不好的做法,但我不知道原因是什么(http://markdaggett.com/blog/2011/12/01/transactions-in-rails/)

在该示例中,如果已创建并保存产品并且客户端更新失败... Rails必须不执行任何操作.

谢谢.

Jim*_*art 25

如果您真的想要,可以在控制器中使用事务.正如你所说,这是不好的做法,但如果你想这样做,只需打电话Product.transaction do而不是transaction do. transaction是一个类方法ActiveRecord::Base,所以你需要在一个ActiveRecord派生类上调用它.您的应用程序中的任何模型类都可以执行此操作(如果您要为不同的模型连接到不同的数据库,那可能不是真的......但您可能不会这样做).

这是一个不好的做法的原因是它没有根据MVC范例正确地分离关注点.您的控制器不应该如此关注您的数据持久性实现.更好的方法是添加方法Product.也许是这样的:

def save_and_update_create_time
  transaction do
    if save
      client.update_attribute(:product_create, Time.now)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

然后拨打电话product.save,而不是在控制器中呼叫product.save_and_update_client_create_time.您可能也需要传递client给该方法; 你的代码client来自哪里不清楚.如果它是一个属性product,那么上面的方法应该有效.

还有更好的,更多的Railsy方法,特别是如果一个人product知道它client而不需要任何控制器数据.然后你可以使用一个after_save回调,像这样(添加到Product类):

after_save :update_client

private

def update_client(product)
  product.client.update_attribute(:product_create, Time.now)
end
Run Code Online (Sandbox Code Playgroud)

然后,每次Product保存时,将更新关联客户端上的字段.您可能必须引入一些代码来检查client第一个的存在.

除了更清晰的代码之外,使用回调的好处是整个回调链与保存一起在单个事务中运行; 您不需要手动创建事务.您可以在Rails文档中阅读有关回调的更多信息.

  • 如果将事务逻辑放入模型中,那么事务不会限制在一个模型中,而不会破坏单独的关注规则吗?通常,交易跨越多个模型的可能性很高,这些模型在数据库级别上不一定相互关联. (4认同)
  • 我认为如果您需要在多个模型中生成事务,则需要聚合根模型.Martin Fowler在这里写了http://martinfowler.com/bliki/DDD_Aggregate.html (2认同)