我有两个模型,一个父级和一个子级(如下所述)。子模型有一个before_save
回调来处理一些外部逻辑,如果它遇到任何错误,回调会使正在保存的模型无效。
class Parent < ActiveRecord::Base
has_one :child
accepts_nested_attributes_for :child
validates :child, :presence => true
validates_associated :child
end
class Child < ActiveRecord::Base
belongs_to :parent
before_save :external_logic
validates :parent, :presence => true
def external_logic
begin
# Some logic
rescue
#Invalidate child model
errors.add(:base, "external logic failed")
return false
end
end
end
我遇到的问题是子模型实例是通过父模型的嵌套属性创建的。当外部逻辑失败时,我希望不保存子模型和父模型,而是自行保存父模型。我怎样才能做到这一点?
请注意,我知道验证回调,但它们不适合这种情况。子模型回调必须是 before_save。
编辑#1
我已经了解交易,并且不要认为有人告诉我“嘿,将它包裹在外部交易中”是有效的回应。这个问题明确地是关于如何通过 before_save 调用来解决这个问题。
为什么我不能在创建时使用验证 - 如评论中所述,需要保证外部逻辑位仅在数据库保存之前运行。无论是否更改数据库记录,验证调用都可能发生多次,因此不适合放置此逻辑。
编辑#2
好的,显然before_save
返回 false 确实会阻止保存父级。我已经通过控制台验证了这一点并实际检查了数据库。但是,我的 rspec 测试告诉我不是这样,这很奇怪。特别是,这是失败的:
describe "parent attributes hash" do
it "creates new record" do
parent = Parent.create(:name => "name", :child_attributes => {:name => "childname"})
customer.persisted?.should be_false
end
end
这可能是 rspec/factory_girl 有点奇怪吗?
编辑#3
测试错误是因为我在 Rspec 中使用了事务性固定装置。这导致测试错误地告诉我对象被持久化在数据库中,而实际上它们并没有。
config.use_transactional_fixtures = true