【发布时间】:2015-10-12 09:33:00
【问题描述】:
我不知道如何创建它,我想创建一个多对多多态关联。
我有一个question 模型,它属于company。
现在问题可以has_many users、groups 或company。取决于你如何分配它。
我希望能够将问题分配给一个/几个用户、一个/几个组或它所属的公司。
我该如何进行设置?
【问题讨论】:
标签: ruby-on-rails
我不知道如何创建它,我想创建一个多对多多态关联。
我有一个question 模型,它属于company。
现在问题可以has_many users、groups 或company。取决于你如何分配它。
我希望能够将问题分配给一个/几个用户、一个/几个组或它所属的公司。
我该如何进行设置?
【问题讨论】:
标签: ruby-on-rails
在这种情况下,我将添加一个分配模型,它充当问题和分配给它的实体之间的交集。
让我们运行一个生成器来创建所需的文件:
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
这只会导致每个受理人类型进行一次查询,这是一个很大的改进。
【讨论】: