所以,我有以下内容:
class Product < ActiveRecord::Base
# Has a bunch of common stuff about assembly hierarchy, etc
end
class SpecializedProduct < Product
# Has some special stuff that a "Product" can not do!
end
有一个制造和装配过程,其中捕获有关产品的数据。在捕获时,最终的产品类型是未知的。在数据库中创建产品记录之后(可能几天之后),可能需要将该产品转换为专用产品并填写其他信息。然而,并非所有产品都会变得专业化。
我一直在尝试使用以下内容:
object_to_change = Product.find(params[:id])
object_to_change.becomes SpecializedProduct
object_to_change.save
然后,当我执行SpecializedProduct.all
时,结果集不包含object_to_change
。相反,object_to_change
仍然在数据库中列为Product
UPDATE "products" SET "type" = ?, "updated_at" = ? WHERE "products"."type" IN ('SpecializedProduct') AND "products"."id" = 30 [["type", "Product"], ["updated_at", Fri, 17 May 2013 10:28:06 UTC +00:00]]
因此,在调用.becomes SpecializedProduct
后,.save
方法现在使用正确的类型,但它无法更新记录,因为更新的WHERE
子句太具体了
我是否真的需要直接访问模型的type
属性?我真的不愿意。
答案 0 :(得分:1)
查看becomes
和becomes!
的来源,它不会改变原始对象。您需要将其分配给新变量:
some_product = Product.find(params[:id])
specialized_product = some_product.becomes SpecializedProduct
specialized_product.save
不确定这将如何处理记录的主键,因此您可能需要进行一些额外的处理以确保您的关系不会受到损害。
答案 1 :(得分:1)
你只需要使用(!)并使用保存的方法获得变成方法的爆炸版本。
两种方法之间的区别:becomes
使用原始对象的所有相同属性值创建新类的新实例。 becomes!
还会更新类型列。
object_to_change = Product.find(params[:id])
object_to_change.becomes! SpecializedProduct
object_to_change.save
答案 2 :(得分:0)
我想不!检查becomes
和becomes!
方法的内容!
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb#L199
看起来您需要使用becomes!
,因为它是becomes
的包装,它也会更改实例的sti列值。
UPD: https://github.com/rails/rails/blob/4-0-stable/activerecord/test/cases/persistence_test.rb#L279 这是代码的测试用例。
<强> UPD2:强> 我想你可以尝试创建另一个类DefaultProject,它是Project的子类,然后你可以将每个从DefaultProject更改为SpecializedProduct,反之亦然
答案 3 :(得分:0)
我有一个类似的问题,我想从一个子类转换到另一个子类。不幸的是,Rails不能优雅地执行此操作,因为它希望使用“where type ='NewSubclass'”来限制保存。即:
UPDATE parents SET type='NewSubclass' WHERE type IN 'NewSubclass' AND id=1234
进入rails,似乎ActiveRecord的lib / active_record / inheritance.rb中的方法名为“finder_needs_type_condition?”被调用并且调用者不够聪明,无法意识到您正在更改类型字段,因此它显然不是那个值。
我在一轮谈判中“解决了这个问题”。我使用ActiveRecord核心作为如何使用属性加载我想要的任何类的实例的基础,并保存它而不必通过完整的ActiveRecord查找堆栈。
old_instance = Parent.find(id) #returns OldSubclass instance
tmp = Parent.allocate.init_with('attributes' => old_instance.attributes)
tmp.type = 'NewSubclass'
tmp.save
Parent.find(id) #returns NewSubclass instance
这真的很难看,我讨厌它。希望有人会考虑在ActiveRecord中修复它。我相信对象随着时间的推移在STI中更改子类会很有用。我有一个包含5个子类的表,因为它可以清理模型。
这是我唯一需要忍受的丑陋。请务必编写正确的测试,以便在ActiveRecord中断此“解决方法”时您可以检测到它。