如何在 Rails(5.0.0.1) 中混合多态性和继承
How can I mix Polymorphism and Inheritance in Rails(5.0.0.1)
我已经尝试了 2 天的解决方案,但一无所获。
我有一个名为 Course
的模型,它包含以下列:
create_table :courses do |c|
c.integer :member_limit
c.string :color
c.float :rating
c.timestamps
end
我还有一个 Content
模型,其中的列 Course
受益于我数据库中的其他模型,例如:
create_table :contents do |c|
c.references :contentable, polymorphic: true, index: true
c.string :title
c.text :description
c.text :script
c.string :cover
c.string :media_type
...
c.integer :creator_id, index: true, foreign_key: :user_id
end
我无法设置 Course < Content
因为我会丢失课程内部的列,例如 member_limit
等等,所以我选择了 Polymorphic。但是,我想避免必须调用 course.content.title
并只写 course.title
但也以相同的方式访问 course.member_limit
并使用 course.save
.[=24= 保存这两个字段]
您推荐的最佳方法是什么?
当前结构。
课程:
class Course < ApplicationRecord
has_one :content, as: :contentable, dependent: :destroy
after_initialize :init
def init
if self.new_record?
self.content ||= build_content
end
end
end
内容:
class Content < ApplicationRecord
belongs_to :creator, optional: true, class_name: 'User'
end
我认为你的前提是错误的,因为你想做的事不应该做。我解释一下:
你想要的,可以通过像这样的辅助方法来完成:
class Course < AR
def title
content.title
end
end
这很丑陋,违反了曾经编写的所有编码参数,而且是一种糟糕的做法。但是,除了一些可以避免必须一个一个地编写辅助方法的元魔法(注意我说写,因为最终它们将被实现),最终结果必须是这样的。没有其他方法可以做到这一点。所以我的回答是,不要这样做。
了解多态性并不容易。它易于实施,但很难知道何时实施。您想要一个包含多个其他资源列的抽象框只是因为。您没有给出创建“......一个包含 Course 从中受益但也使我的数据库中的其他模型受益的列的内容模型”的决定背后的原因。我问。为什么?有什么好处?为什么这样做?它是否像预期的那样工作?它更容易使用和扩展吗?它被封装了吗?是 dry、rest、solid 以及这些天使用的所有其他首字母缩略词吗?
简短的回答是否定的。它不是。所以不要这样做。让课程 class 有标题。如果另一个 table 也有标题,那又怎样?你知道全世界有多少table有"name"这个栏目吗?如果我们都想以您的方式实现它,我们将有这样的东西:
class School < Ar
belongs_to :name
def full_name
name.body
end
end
class Student < Ar
belongs_to :name
def full_name
name.body
end
end
这显然没有用。那么,如果它对一列没有用,为什么它对更多列有用?
结论:
- 多态性不用于 re-use 列,从不。
- STI 用于 re-using 列(某种程度上)
- 多态可以理解为可以附加到多个资源上的东西。例如:通知、日志、地址……基本上说课程有地址,学生也有地址。学生和课程都是可寻址的。但是永远,永远,永远不要说内容是一种地址。这在任何方面、形状或形式上都是不正确的。
- 在 STI 中,资源成为 parent 的一种类型,因此在 STI 中,课程将是一种内容。学生也将是一种内容。但是永远,永远,永远不要说课程有内容。这在任何方面、形状或形式上都是不正确的。
在 STI 中,内容 table 将包含课程的所有列以及任何其他 table 想要成为课程的所有列。对于本质上在数据中相同或几乎相同(看起来很多你描述的)并且你不想在所有 children classes 中重复代码的资源来说,这是一个很好的做法。通过在每个 child class.
中重复 parent class 代码,可以在没有 STI 的情况下实现所有 STI
所以,简短的回答(是不是有点晚了?)。不,你不能为所欲为。你可以
- 只需重复每个资源中的列(首次实施的首选方式,简单、快速且标准)
- 使用 STI 和 ONE TABLE 表示将具有 "shared" 内容信息的所有资源
- 使用多态性并使用辅助方法来做你想做的事。但是不要忘记访问器不是唯一的访问器。想想
def title=
这样的方法。你要 re-implement 所有人吗?
抱歉篇幅太长了,刚刚看到这个错误太多次了。
我已经尝试了 2 天的解决方案,但一无所获。
我有一个名为 Course
的模型,它包含以下列:
create_table :courses do |c|
c.integer :member_limit
c.string :color
c.float :rating
c.timestamps
end
我还有一个 Content
模型,其中的列 Course
受益于我数据库中的其他模型,例如:
create_table :contents do |c|
c.references :contentable, polymorphic: true, index: true
c.string :title
c.text :description
c.text :script
c.string :cover
c.string :media_type
...
c.integer :creator_id, index: true, foreign_key: :user_id
end
我无法设置 Course < Content
因为我会丢失课程内部的列,例如 member_limit
等等,所以我选择了 Polymorphic。但是,我想避免必须调用 course.content.title
并只写 course.title
但也以相同的方式访问 course.member_limit
并使用 course.save
.[=24= 保存这两个字段]
您推荐的最佳方法是什么?
当前结构。
课程:
class Course < ApplicationRecord
has_one :content, as: :contentable, dependent: :destroy
after_initialize :init
def init
if self.new_record?
self.content ||= build_content
end
end
end
内容:
class Content < ApplicationRecord
belongs_to :creator, optional: true, class_name: 'User'
end
我认为你的前提是错误的,因为你想做的事不应该做。我解释一下:
你想要的,可以通过像这样的辅助方法来完成:
class Course < AR
def title
content.title
end
end
这很丑陋,违反了曾经编写的所有编码参数,而且是一种糟糕的做法。但是,除了一些可以避免必须一个一个地编写辅助方法的元魔法(注意我说写,因为最终它们将被实现),最终结果必须是这样的。没有其他方法可以做到这一点。所以我的回答是,不要这样做。
了解多态性并不容易。它易于实施,但很难知道何时实施。您想要一个包含多个其他资源列的抽象框只是因为。您没有给出创建“......一个包含 Course 从中受益但也使我的数据库中的其他模型受益的列的内容模型”的决定背后的原因。我问。为什么?有什么好处?为什么这样做?它是否像预期的那样工作?它更容易使用和扩展吗?它被封装了吗?是 dry、rest、solid 以及这些天使用的所有其他首字母缩略词吗?
简短的回答是否定的。它不是。所以不要这样做。让课程 class 有标题。如果另一个 table 也有标题,那又怎样?你知道全世界有多少table有"name"这个栏目吗?如果我们都想以您的方式实现它,我们将有这样的东西:
class School < Ar
belongs_to :name
def full_name
name.body
end
end
class Student < Ar
belongs_to :name
def full_name
name.body
end
end
这显然没有用。那么,如果它对一列没有用,为什么它对更多列有用?
结论:
- 多态性不用于 re-use 列,从不。
- STI 用于 re-using 列(某种程度上)
- 多态可以理解为可以附加到多个资源上的东西。例如:通知、日志、地址……基本上说课程有地址,学生也有地址。学生和课程都是可寻址的。但是永远,永远,永远不要说内容是一种地址。这在任何方面、形状或形式上都是不正确的。
- 在 STI 中,资源成为 parent 的一种类型,因此在 STI 中,课程将是一种内容。学生也将是一种内容。但是永远,永远,永远不要说课程有内容。这在任何方面、形状或形式上都是不正确的。
在 STI 中,内容 table 将包含课程的所有列以及任何其他 table 想要成为课程的所有列。对于本质上在数据中相同或几乎相同(看起来很多你描述的)并且你不想在所有 children classes 中重复代码的资源来说,这是一个很好的做法。通过在每个 child class.
中重复 parent class 代码,可以在没有 STI 的情况下实现所有 STI所以,简短的回答(是不是有点晚了?)。不,你不能为所欲为。你可以
- 只需重复每个资源中的列(首次实施的首选方式,简单、快速且标准)
- 使用 STI 和 ONE TABLE 表示将具有 "shared" 内容信息的所有资源
- 使用多态性并使用辅助方法来做你想做的事。但是不要忘记访问器不是唯一的访问器。想想
def title=
这样的方法。你要 re-implement 所有人吗?
抱歉篇幅太长了,刚刚看到这个错误太多次了。