ActiveRecord - :belongs_to多个类。重新配置行为

时间:2015-05-24 06:23:41

标签: ruby-on-rails activerecord configuration has-many belongs-to

我已经构建了以下但没有使用Rails模型。 我有以下模型,没有任何指定的关系。 故事 目的 文化 道德 值 书 关键词 字符

我手动将所有关系信息作为数组存储在单个关系表中,其结构如下所示 for is the table name / model name pluralized and for_id is the object  id

我想做的是这样的事情 -     模型:     故事 - > has_many :: tale_relations     目的 - > has_many :: tale_relations     文化 - > has_many :: tale_relations     道德 - > has_many :: tale_relations     值 - > has_many :: tale_relations     书籍 - > has_many :: tale_relations     关键字 - > has_many :: tale_relations     字符 - > has_many :: tale_relations

Model: TaleRelation ->
belongs_to: :tale
belongs_to: :purpose
belongs_to: :culture
belongs_to: :morals
belongs_to: :values
belongs_to: :books
belongs_to: :keywords
belongs_to: :characters

但是不希望tale_relations表为这些不同的模型中的每一个都有多个列 我的意思是我不想要tale_id,purpose_id,moral_id等等(8个这样的专栏) 希望能够通过表名(:for column)和id#(:for_id column)来识别记录。

目前它正在手动工作,但只是认为代码实际上变得干净简单,并且实际上可扩展,如果我可以使这个工作。只有两列可以识别任何关系。

1 个答案:

答案 0 :(得分:0)

如果您在模型上施加额外的结构,我怀疑您的问题会变得容易多了。您调用模型TaleRelation这一事实让我认为大多数这些事情应该是直接相关的故事,这可以通过has_and_belongs_to_many关系完成,然后其他事情可能与through: :tale相关。但我认为我们也可以解决上述问题。

首先,您要查看polymorphic associations。在上面发布的代码中,第一步是在TaleRelation中使用@calendar.posts,在其他模型中使用belongs_to :relatable, polymorphic: true。这要求tale_relations表有两列relatable_id和relatable_type,这就是你要为for和for_id做的事情。

但是我认为你真的想进一步采取这一步,并摆脱故事,人物,价值观等列。我们可以使用两个多态关系而不仅仅是一个。

首先,表结构(4列):

has_many :tale_relations, as: :relatable

和模型

create_table :tale_relations do |t|
  t.integer :relater_id
  t.string :relater_type
  t.integer :relatee_id
  t.string :relatee_type
end

我正在使用回调进行的操作是每次使用给定的关联器创建或销毁TaleRelation并且相关时,它会创建或销毁相应的反转。因此,如果给定的Tale与Character相关,那么Character也与Tale相关。如果你不想要这种对称性,那就把这些回调拿出来。

现在,例如,在故事:

class TaleRelation < ActiveRecord::Base
  belongs_to :relater, polymorphic: true
  belongs_to :relatee, polymorphic: true

  after_create :create_inverse_relationship
  after_destroy :destroy_inverse_relationship

  def create_inverse_relationship
    self.class.find_or_create_by(inverse_attributes)
  end

  def destroy_inverse_relationship
    self.class.find_by(inverse_attributes).try(:destroy)
  end

  def inverse_attributes
    {
      relater_id: relatee_id,
      relater_type: relatee_type,
      relatee_id: relater_id,
      relatee_type: relater_type
    }
  end
end

和“价值与品格”中的类似事物等等。现在是一个故事has_many:字符,无需将它们的ID存储为逗号分隔的字符串或数组,因为它看起来就像你在表格中所做的那样。确保不要包含任何自引用的has_many语句(即不要将class Tale < ActiveRecord::Base has_many :tale_relations, as: :relater, dependent: :destroy has_many :characters, through: :tale_relations, source: :relatee, source_type: "Character" has_many :values, through: :tale_relations, source: :relatee, source_type: "Value" # and so on end 放入Character中)

因此我们通过多态relater_id和relater_type定义与TaleRelation的关系,因此我们可以拥有所有这些关系而不需要所有列。通过这种关系,我们可以(多态地)收集正确类的所有相关性。这给了我:

has_many :characters

请注意,这不会传播二阶关系。即,

2.2.2 > t = Tale.create(name: "T")
 => #<Tale id: 1, name: "T">
2.2.2 > c = Character.create(name: "C")
 => #<Character id: 1, name: "C">
2.2.2 > d = Character.create(name: "D")
 => #<Character id: 2, name: "D">
2.2.2 > v = Value.create(name: "V")
 => #<Value id: 1, name: "V">
2.2.2 > t.characters << c
 => #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">]>
2.2.2 > v.characters << c
 => #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">]>
2.2.2 > v.characters << d
 => #<ActiveRecord::Associations::CollectionProxy [#<Character id: 1, name: "C">, #<Character id: 2, name: "D">]>
2.2.2 > d.values
 => #<ActiveRecord::Associations::CollectionProxy [#<Value id: 1, name: "V">]>
2.2.2 > t.characters.first.values
 => #<ActiveRecord::Associations::CollectionProxy [#<Value id: 1, name: "V">]>

即使t有一个带有值的字符。一旦你开始这样做,很快你就会拥有与其他一切相关的一切。