基本上我想要做的是在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
在这两种情况下,验证失败时都会回滚创建,因为它位于验证事务中。当然,我也想在验证失败时记录。我真的不想登录文件或数据库以外的其他地方,因为我需要日志条目与其他模型的关系以及执行请求的能力。
我有什么选择?
答案 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文档讨论了如何完成此任务。