繁体   English   中英

Rails / ActiveRecord:在一侧是多态的情况下建立多对多关联的正确方法是什么?

[英]Rails/ActiveRecord: What is the correct way to establish a many to many association where one side is polymorphic?

我有BadgeCommentProject 注释和项目可以具有许多标志。 徽章可以包含许多注释和项目。

徽章通过名为“反应”的联接表指向注释和项目。

# reaction.rb
class Reaction < ApplicationRecord
  belongs_to :badge
  belongs_to :reaction_target, polymorphic: true
end

# badge.rb
class Badge < ApplicationRecord
  has_many :reactions
  has_many :reaction_targets, through: :reactions
end

# comment.rb
class Comment < ApplicationRecord
  has_many :reactions, as: :reaction_target
  has_many :badges, through: :reactions
end

# project.rb
class Project < ApplicationRecord
  has_many :reactions, as: :reaction_target
  has_many :badges, through: :reactions
end

我现在可以将徽章添加到反应目标:

> @comment.badges << Badge.first_or_create(name: "test")
=> [#<Badge:0x00007fcff7619d28
  id: 1,
  name: "test",
  created_at: Sat, 23 Feb 2019 18:25:54 UTC +00:00,
  updated_at: Sat, 23 Feb 2019 18:25:54 UTC +00:00>]

但是我不能做相反的事情:

> Badge.first_or_create(name: "test").reaction_targets << @comment
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have
a has_many :through association 'Badge#reaction_targets' on the
polymorphic object 'ReactionTarget#reaction_target' without 'source_type'.
Try adding 'source_type: "ReactionTarget"' to 'has_many :through'
definition. from /Users /elephant/.rvm/gems/ruby-2.6.0/gems/activerecord-5
.2.2/lib/active_record/reflecti on.rb:932:in `check_validity!'

我不太确定为什么在我的关联应该是多态的时建议我指定源类型。 但是,我尝试过:

class Badge < ApplicationRecord
  has_many :reactions
  has_many :reaction_targets, through: :reactions, source_type: "ReactionTarget"
end

但是然后我得到一个错误:

> Badge.first_or_create(name: "test").reaction_targets
NameError: uninitialized constant Badge::ReactionTarget

我正在努力解决这个问题。 我想念什么? 我哪里出问题了? 是什么使我无法从徽章方面确定反应目标?

TL; DR:

app / models / badge.rb

class Badge < ApplicationRecord
  has_many :reactions

  has_many :reaction_target_comments, through: :reactions, source: :reaction_target, source_type: 'Comment'
  has_many :reaction_target_projects, through: :reactions, source: :reaction_target, source_type: 'Project '
  # ... etc
end

说明

据我了解的多态多对多关联,您不能

class Badge < ApplicationRecord
  has_many :reaction_targets, through: :reactions
  # ...
end

...(将产生一个错误,就像您所观察到的一样),因为如果可能的话,这意味着执行badge.reaction_targets应该返回一个“差异模型” badge.reaction_targets数组,即:

badge = Badge.find(1)
puts badge.reaction_targets.to_a
# => [<Comment id: 1>,
#     <Project id: 45>,
#     <SomeModel id: 99>,
#     <COmment id: 3>,
#     ...]

^上面的代码看起来非常直观,对吗? ...因为它应该返回不同类型记录的数组,这是合理的,因为它是多态关系,对吗? 是的,这是完全正确的,但是在生成什么SQL字符串方面却出现了问题。 请参阅下面的等效SQL示例:

puts badge.reaction_targets
# =>  SELECT "WHAT_TABLE_1".* FROM "WHAT_TABLE_1" INNER JOIN reactions ...
#     SELECT "WHAT_TABLE_2".* FROM "WHAT_TABLE_2" INNER JOIN reactions ...
#     SELECT "WHAT_TABLE_3".* FROM "WHAT_TABLE_3" INNER JOIN reactions ...
#     ... etc

^ ...因为reaction_targets预计将具有不同的模式,实例,然后想象应该需要什么上面的SQL执行获得的所有记录? 虽然,我认为有可能获得所有这些,但它可能不是直接的SQL语句,而是可能具有一些应用程序端Ruby代码逻辑的组合。 而且,返回类型也不应该是ActiveRecord::Associations::CollectionProxy ,而应该是专门用于满足has_many多态关系的新型对象。 我的意思是,假设您是否执行以下类似操作:

badge.reaction_targets.where(is_enabled: true, first_name: 'Hello')
# or even more complex:
badge.reaction_targets.joins(:users).where(users: { email: 'email@example.com' }).

^ ...我认为对于多态连接,上面没有一个直接的SQL语句,所以这也许就是为什么您需要为不同的“模型”添加sourcesource_type的原因,就像我上面的回答一样。

可能的解决方法

如果可以返回一个Array对象而不是普通的ActiveRecord::Associations::CollectionProxy对象,则可以执行以下操作。 (尽管您仍然必须指定每个多态的has_many关系,并且将来可能会添加更多。)

class Badge < ApplicationRecord
  has_many :reactions

  has_many :reaction_target_comments, through: :reactions, source: :reaction_target, source_type: 'Comment'
  has_many :reaction_target_projects, through: :reactions, source: :reaction_target, source_type: 'Project'

  def reaction_targets
    reaction_target_comments.to_a + reaction_target_projects.to_a
  end
end

用法示例:

badge = Badge.find(1)
puts badge.reaction_targets
# =>[<Comment id: 1>,
#    <Comment id: 45>,
#    <Project id: 99>,
#    <Project id: 3>,
#    ...]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM