我有一个使用acts_as_nested_set
分叉的模型,我已经为模型添加了一个方法来保存模型并在一个事务中将节点移动到集合中。此方法调用验证方法以确保移动有效,返回true或false。如果验证失败,我希望我的save方法引发ActiveRecord::Rollback
以回滚事务,但也向调用者返回false。
我的模型看起来像这样:
class Category < ActiveRecord::Base
acts_as_nested_set :dependent => :destroy, :scope => :journal
def save_with_place_in_set(parent_id)
Category.transaction do
return false if !save_without_place_in_set
if !validate_move parent_id
raise ActiveRecord::Rollback and return false
else
place_in_nested_set parent_id
return true
end
end
end
alias_method_chain :save, :place_in_set
def validate_move(parent_id)
# return true or false if the move is valid
# ...
end
def place_in_nested_set(parent_id)
# place the node in the correct place in the set
# ...
end
end
但是,当我在失败的情况下调用save时,事务将回滚但函数返回nil
:
>> c = Category.new(:name => "test")
=> #<Category id: nil, name: "test" parent_id: nil, lft: nil, rgt: nil>
>> c.save_with_place_in_set 47
=> nil
>> c.errors.full_messages
=> ["The specified parent is invalid"]
答案 0 :(得分:27)
您可以将要从函数返回的值存储在变量中,并将其返回到事务块之外。 E.g。
def save_with_place_in_set(parent_id)
return_value = false
Category.transaction do
if !save_without_place_in_set
return_value = false
elsif !validate_move parent_id
return_value = false
raise ActiveRecord::Rollback
else
place_in_nested_set parent_id
return_value = true
end
end
return return_value
end
我最初将return_value设置为false,因为如果其他方法之一提升ActiveRecord::Rollback
,我认为可以从该事务块中解脱出来。
答案 1 :(得分:11)
因为ActiveRecord::Rollback
异常被处理,但没有被ActiveRecord::Transaction
重新引发,所以我可以将我的返回移出事务块,从而在事务回滚后返回一个值。 / p>
进行一些重构:
def save_with_place_in_set(parent_id = nil)
Category.transaction do
return false if !save_without_place_in_set
raise ActiveRecord::Rollback if !validate_move parent_id
place_in_nested_set parent_id
return true
end
return false
end
答案 2 :(得分:0)
我知道这可能有点晚了,但我遇到了同样的问题并且刚刚发现,在事务块中你可以简单地引发一个Exception并拯救那个...... Rails隐式地回滚整个事务。所以不需要ActiveRecord :: Rollback。
例如:
def create
begin
Model.transaction do
# using create! will cause Exception on validation errors
record = Model.create!({name: nil})
check_something_afterwards(record)
return true
end
rescue Exception => e
puts e.message
return false
end
end
def check_something_afterwards(record)
# just for demonstration purpose
raise Exception, "name is missing" if record.name.nil?
end
我正在使用Rails 3.2.15和Ruby 1.9.3。