如何与STI建立ActiveRecord关联

时间:2013-03-07 06:36:24

标签: ruby-on-rails associations sti

我遇到了AR试图建立从其他人继承的模型关联的问题。问题是在调用save方法之前,相关模型正在保存到数据库中。

我在此页http://techspry.com/ruby_and_rails/active-records-or-push-or-concat-method/

中找到了更多信息

这真的很奇怪,为什么AR会自动保存附加到关联的模型(使用<<方法)?很明显,即使父元素已经存在,也必须调用save方法。我们可以阻止这个呼叫

@user.reviews.build(good_params)

但是在关联具有层次结构的上下文中这将是一个问题,例如:如果Hunter has_many:animals,而Dog和Cat继承自Animal,我们无法做到

@hunter.dogs.build
@hunter.cats.build 

相反,我们坚持

@hunter.animals << Cat.new
@hunter.animals << Dog.new 

如果Cat / Dog类没有验证,该对象将自动保存到数据库中。我该如何防止这种行为?

2 个答案:

答案 0 :(得分:9)

我发现Rails 3并不完全支持与STI的关联,并且通常需要黑客攻击。阅读此帖http://simple10.com/rails-3-sti/的更多信息。正如其中一条评论中所述,此问题在rails 4 https://github.com/rails/rails/commit/89b5b31cc4f8407f648a2447665ef23f9024e8a5中提及 Rails sux如此糟糕的处理继承=((Hope Rails 4修复了这个问题。

与此同时,我正在使用这个丑陋的解决方法:

animal = @hunter.animals.build type: 'Dog' 

然后替换构建的对象,此步骤可能需要反思到锻炼(查看Lucy的回答和评论)

hunter.animals[@hunter.animals.index(animal)] = animal.becomes(Dog)

这将在此上下文中正确运行,因为

hunter.animals[@hunter.animals.index(animal)].is_a? Dog

将返回true,并且不会使用赋值进行数据库调用

答案 1 :(得分:5)

根据Gus的回答,我实施了类似的解决方案:

# instantiate a dog object
dog = Dog.new(name: 'fido')

# get the attributes from the dog, add the class (per Gus's answer)
dog_attributes = dog.attributes.merge(type: 'Dog')

# build a new dog using the correct attributes, including the type
hunter.animals.build(dog_attributes)

请注意,原始的狗对象会被丢弃。根据您需要设置的属性数量,可能更容易做到:

hunter.animals.build(type: 'Dog', name: 'Fido')