gpt4 book ai didi

ruby-on-rails - 通过子 before_save 回调使父模型保存无效

转载 作者:行者123 更新时间:2023-12-04 02:11:22 25 4
gpt4 key购买 nike

我有两个模型,一个父模型和一个子模型(如下所述)。子模型有一个 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

最佳答案

好的,所以你的问题是 ActiveRecord::Callbacks命令。

正如您在链接页面上看到的那样,第一次验证 已处理,如果验证成功,则运行 before_save 回调before_save 是一个你可以假设每个验证都通过的地方,这样你就可以操作位数据或填充基于其他属性的自定义属性。诸如此类。

所以你可以做的就是对 Child 模型说:
validate :external_logic 并删除 before_save :external_logic 回调。

相当于你想做的事情。创建 Parent 实例时,如果 Child 对象验证失败,它只会出错,这将在您的 :external_logic 验证方法中发生。这是 custom validation method technique .

OP更新后:

你仍然可以使用 :validate 方法。您可以将其设置为仅在 create 上运行:
验证 :external_logic, :on => :create

如果您遇到问题,您也需要它在 update 上运行,这是默认行为。验证仅在 .create 和 .update 上运行。

或者如果你想坚持 before_save:

整个回调链都包含在一个事务中。如果任何 before 回调方法完全返回 false 或引发异常,则执行链将停止并发出 ROLLBACK; after 回调只能通过引发异常来实现。

我看到您确实返回了 false 所以它应该按预期工作。你如何使用 Parent.create! 方法?那里有什么论据?

确保你像这样使用它(假设 .name 是 Parent 和 Child 的一个属性):

Parent.create!{
:name => 'MyParent'
# other attributes for Parent
:child_attributes => {
:name => 'MyChild'
# other attributes for Child
}
}

这样,父对象和子对象将在同一个事务中创建,因此如果您的before_save 方法返回 false,父对象将被回滚。

如果您不能使用这种格式,您可以尝试使用纯交易(docexample in guides):

Parent.transaction do
p = Parent.create
raise Exception if true # any condition
end

如果 block 内出现异常,您在此事务内所做的任何操作都将被回滚。

关于ruby-on-rails - 通过子 before_save 回调使父模型保存无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17095405/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com