多对多的多态关联

时间:2015-10-12 09:33:01

标签: ruby-on-rails

我不知道如何创建它,我想创建一个多对多的多态关联。

我有kjh 模型,属于question

现在问题可以是companyhas_many usersgroups。取决于您如何分配它。

我希望能够将问题分配给一个/多个用户,或一个/多个组,或者它所属的公司。

我该如何设置呢?

1 个答案:

答案 0 :(得分:3)

在这种情况下,我会添加一个Assignment模型,它充当问题与分配给它的实体之间的交集。

创建表

让我们运行一个生成器来创建所需的文件:

rails g model assignment question:belongs_to assignee_id:integer assignee_type:string

然后,让我们打开创建的迁移文件(db/migrations/...__create_assignments.rb)

class CreateAssignments < ActiveRecord::Migration
  def change
    create_table :assignments do |t|
      t.integer :assignee_id
      t.string :assignee_type
      t.belongs_to :question, index: true, foreign_key: true
      t.index [:assignee_id, :assignee_type]
      t.timestamps null: false
    end
  end
end

如果您在此处注意,可以看到我们为question_id但不是assignee_id添加了外键。这是因为数据库不知道哪个表assignee_id指向并且不能强制引用完整性*。我们还为[:assignee_id, :assignee_type]添加了一个复合索引,因为它们总是会被一起查询。

建立关系

class Assignment < ActiveRecord::Base
  belongs_to :question
  belongs_to :assignee, polymorphic: true
end

polymorpic: true选项告诉ActiveRecord查看assignee_type列,以决定从中加载assignee的表。

class User < ActiveRecord::Base
  has_many :assignments, as: :assignee
  has_many :questions, through: :assignments
end

class Group < ActiveRecord::Base
  has_many :assignments, as: :assignee
  has_many :questions, through: :assignments
end    

class Company < ActiveRecord::Base
  has_many :assignments, as: :assignee
  has_many :questions, through: :assignments
end

不幸的是,多态关系的一个警告是你不能急于加载多态的受让人关系。或者声明has_many :assignees, though: :assignments

一种解决方法是:

class Group < ActiveRecord::Base
  has_many :assignments, as: :assignee
  has_many :questions, through: :assignments

  def assignees
    assignments.map(&:assignee)
  end
end  

但这会导致效率非常低的SQL查询,因为每个受理人都会被加载到查询中!

相反,你可以这样做:

class Question < ActiveRecord::Base
  has_many :assignments

  # creates a relationship for each assignee type
  ['Company', 'Group', 'User'].each do |type|
    has_many "#{type.downcase}_assignees".to_sym,
        through: :assignments,
        source: :assignee,
        source_type: type
  end

  def assignees
    (company_assignees + group_assignees + user_assignees)
  end
end

每个受让人类型只会导致一次查询,这是一个很大的改进。