使用 Rails 7
我正在使用单表继承 (STI) 来存储一些非常简单的关联。源对象使用 has_many 与 STI 模型的关联。按照https://stackoverflow.com/a/45681641/1014251中的一些建议,我在连接模型中使用了多态关系。这确实很有效,但有一个烦恼:
创建连接模型时,源类型取自根 STI 类而不是实际源。
模型:
class Guidance < ApplicationRecord
has_many :guidance_details
has_many :themes, through: :guidance_details, source: :detailable, source_type: "Theme"
end
class Detail < ApplicationRecord
has_many :guidance_details, as: :detailable
has_many :guidances, through: :guidance_details
end
class GuidanceDetail < ApplicationRecord
belongs_to :detailable, polymorphic: true
belongs_to :guidance
end
class Theme < Detail
end
问题
如果我创建一个新的 GuidanceDetail 并且不指定系统,则detailable_source
系统会插入“Detail”而不是“Theme”:
guidance_detail = GuidanceDetail.create(guidance: Guidance.first, detailable: Theme.first)
guidance_detail.detailable_type => "Detail"
可详细显示的类型应为“主题”。
为了解决这个问题,目前我每次创建新的 GuidanceDetail 时都必须指定 detailable_type。
无效的修复
我曾尝试直接指定子对象的 has_many 关联,但得到相同的结果:
class Theme < Detail
has_many :guidance_details, as: :detailable
end
替代创建方法不起作用
theme = Theme.first
guidance = Guidance.first
guidance.themes << theme
输出:
GuidanceDetail Create (1.3ms) INSERT INTO "guidance_details" ("detailable_id", "guidance_id", "position", "created_at", "updated_at", "detailable_type") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["detailable_id", 11], ["guidance_id", 1], ["position", nil], ["created_at", "2024-11-14 10:24:19.785623"], ["updated_at", "2024-11-14 10:24:19.785623"], ["detailable_type", "Detail"]]
如您所见:“detailable_type”是“Detail”。
据我了解,为了存储类名
"Theme"
,它似乎Detail
需要一个abstract_class
。ActiveRecord::Associations::BelongsToPolymorphicAssociation将设置
foreign_type
为polymorphic_name
polymorphic_name
定义为:和
base_class
因此,您可以看到,
base_name
只有当类是基类或超类是抽象类时,才会使用实际的类名。至少这是我跟踪它的方式。您很可能实际上并不需要/不想要多态关联。
多态关联解决了关联不仅指向多个类而且指向多个表的问题。比如说,我们有:
我们需要一个
wheels.vehicle_id
和wheels.vechicle_type
列,因为wheels.vehicle_id
可以指向任何表,并且两者的组合构成了一种复合外键。如果我们改变这一点,使 Car 和 Airplane 使用单表继承从 Vehicle 继承,我们就不再需要多态关联,因为我们只有一个表:
此处 wheel 实际上并不关心 Vehicle 是汽车还是飞机。只是 vehicle 关联指向 vehicles 表,并且可以通过加入 来解决
wheels.vehicle_id = vehicles.id
。这是一个非常有力的论据,说明了为什么您要使用单表继承,因为它可以让您拥有真正的外键约束,而不是仅仅诉诸于解决对象关系阻抗不匹配问题的黑客手段。
当记录被加载时,Rails 将读取该
vehicles.type
列并提供正确的子类型。