如何在 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

这显然没有用。那么,如果它对一列没有用,为什么它对更多列有用?

结论:

  1. 多态性不用于 re-use 列,从不。
  2. STI 用于 re-using 列(某种程度上)
  3. 多态可以理解为可以附加到多个资源上的东西。例如:通知、日志、地址……基本上说课程有地址,学生也有地址。学生和课程都是可寻址的。但是永远,永远,永远不要说内容是一种地址。这在任何方面、形状或形式上都是不正确的。
  4. 在 STI 中,资源成为 parent 的一种类型,因此在 STI 中,课程将是一种内容。学生也将是一种内容。但是永远,永远,永远不要说课程有内容。这在任何方面、形状或形式上都是不正确的。

在 STI 中,内容 table 将包含课程的所有列以及任何其他 table 想要成为课程的所有列。对于本质上在数据中相同或几乎相同(看起来很多你描述的)并且你不想在所有 children classes 中重复代码的资源来说,这是一个很好的做法。通过在每个 child class.

中重复 parent class 代码,可以在没有 STI 的情况下实现所有 STI

所以,简短的回答(是不是有点晚了?)。不,你不能为所欲为。你可以

  1. 只需重复每个资源中的列(首次实施的首选方式,简单、快速且标准)
  2. 使用 STI 和 ONE TABLE 表示将具有 "shared" 内容信息的所有资源
  3. 使用多态性并使用辅助方法来做你想做的事。但是不要忘记访问器不是唯一的访问器。想想def title=这样的方法。你要 re-implement 所有人吗?

抱歉篇幅太长了,刚刚看到这个错误太多次了。