7

所以,我有以下内容:

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模型的属性吗?我真的宁愿不要。

4

4 回答 4

2

查看 and 的来源becomesbecomes!它不会改变原始对象。您需要将其分配给一个新变量:

some_product = Product.find(params[:id])
specialized_product = some_product.becomes SpecializedProduct
specialized_product.save

不过,不确定这将如何处理记录的主键,因此您可能需要做一些额外的检查以确保您的关系不会受到破坏。

于 2013-08-12T15:32:17.807 回答
2

您只需要带有 (!) 的变成方法的 bang 版本并保存。

两种方法的区别:becomes创建一个新类的新实例,其属性值与原始对象的所有相同。 becomes!还会更新类型列。

object_to_change = Product.find(params[:id])
object_to_change.becomes! SpecializedProduct
object_to_change.save
于 2014-11-03T22:52:19.670 回答
0

我有一个类似的问题,我想从一个子类更改为另一个子类。不幸的是,Rails 并没有优雅地做到这一点,因为它想用“where type='NewSubclass'”来限制保存。IE:

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 破坏这个“解决方法”时,你可以检测到它。

于 2014-10-30T18:18:11.940 回答
0

我想不是!检查来源becomesbecomes!方法! 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,反之亦然

于 2013-05-20T18:57:22.103 回答