ActiveRecord验证失败后如何将数据保存到数据库?

时间:2010-10-07 11:18:44

标签: ruby-on-rails ruby validation activerecord

基本上我想要做的是在MyModelLog表中的MyModel上记录一个动作。这是一些伪代码:

class MyModel < ActiveRecord::Base
  validate :something

  def something
     # test
     errors.add(:data, "bug!!")
  end
end

我的模型看起来像这样:

class MyModelLog < ActiveRecord::Base

  def self.log_something
    self.create(:log => "something happened")
  end

end

为了记录我试图:

  • MyModelLog.log_something

  • something方法中添加MyModel
  • MyModelLog.log_something

  • after_validation回调上致电MyModel

在这两种情况下,验证失败时都会回滚创建,因为它位于验证事务中。当然,我也想在验证失败时记录。我真的不想登录文件或数据库以外的其他地方,因为我需要日志条目与其他模型的关系以及执行请求的能力。

我有什么选择?

6 个答案:

答案 0 :(得分:8)

嵌套事务似乎在MySQL中有效。

以下是我在新生成的rails(使用MySQL)项目上尝试的内容:

./script/generate model Event title:string --skip-timestamps --skip-fixture

./script/generate model EventLog error_message:text --skip-fixture

class Event < ActiveRecord::Base                                                                                                                                       
  validates_presence_of :title                                                                                                                                         
  after_validation_on_create :log_errors                                                                                                                               

  def log_errors                                                                                                                                                       
    EventLog.log_error(self) if errors.on(:title).present?                                                                                                             
  end                                                                                                                                                                  
end  

class EventLog < ActiveRecord::Base                                                                                                                                    
  def self.log_error(event)                                                                                                                                            
    connection.execute('BEGIN') # If I do transaction do then it doesn't work.
    create :error_message => event.errors.on(:title)                                                                                            
    connection.execute('COMMIT')                                                                                                                                       
  end                                                                                                                                                                  
end 

# And then in script/console:
>> Event.new.save
=> false
>> EventLog.all
=> [#<EventLog id: 1, error_message: "can't be blank", created_at: "2010-10-22 13:17:41", updated_at: "2010-10-22 13:17:41">]
>> Event.all
=> []

也许我过度简化了它,或者遗漏了一些观点。

答案 1 :(得分:3)

这是否适合Observer?我不确定,但我希望在交易之外存在...我有类似的需求,我可能想删除更新中的记录......

答案 2 :(得分:2)

我通过利用Ruby的变量范围解决了这样的问题。基本上我在事务块之外声明了一个error变量然后捕获,存储日志消息,并再次引发错误。

它看起来像这样:

def something
    error = nil
    ActiveRecord::Base.transaction do
        begin
            # place codez here
        rescue ActiveRecord::Rollback => e
            error = e.message
            raise ActiveRecord::Rollback
        end
    end
    MyModelLog.log_something(error) unless error.nil?
end

通过在事务范围之外声明error变量,即使在事务退出后,变量的内容仍然存在。

答案 3 :(得分:1)

我不确定它是否适用于您,但我假设您正在尝试从控制器保存/创建模型。在控制器中,很容易检查该操作的结果,并且您很可能已经为用户提供了有用的闪存;所以你可以在那里轻松记录相应的消息。

我还假设您不使用任何显式事务,因此如果您在控制器中处理它,它就在事务之外(每次保存和销毁在他们自己的事务中工作)。

您怎么看?

答案 4 :(得分:1)

MyModelLog.log_something应该使用不同的连接完成。

您可以使用 establish_connection 使MyModelLog模型始终使用不同的连接。

class MyModelLog < ActiveRecord::Base
  establish_connection Rails.env # Use different connection

  def self.log_something
    self.create(:log => "something happened")
  end
end

不确定这是否是正确的记录方式!!

答案 5 :(得分:0)

您可以使用嵌套事务。这样,回调中的代码在与失败验证不同的事务中执行。 ActiveRecord::Transactions::ClassMethods的Rails文档讨论了如何完成此任务。