9

我在控制器中有一个示例动作。

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

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

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

但它会产生这个错误:

undefined method `transaction'

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

在示例中,如果产品已创建并保存并且客户端更新失败……Rails 不能什么都不做。

谢谢。

4

1 回答 1

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

然后,不要调用product.save您的控制器,而是调用product.save_and_update_client_create_time. 您可能也需要传递client给该方法;从您的代码中不清楚来自哪里client。如果它是 on 的一个属性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

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

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

于 2013-03-27T13:13:55.473 回答