我有两个使用people
表的模型:Person
和Person::Employee
(继承自Person
)。 people
表格有type
列。
还有另一个模型Group
,它具有称为:owner
的多态关联。 groups
表格同时包含owner_id
列和owner_type
列。
应用程序/模型/ person.rb:
class Person < ActiveRecord::Base
has_one :group, as: :owner
end
应用程序/模型/人/ employee.rb:
class Person::Employee < Person
end
应用程序/模型/ group.rb:
class Group < ActiveRecord::Base
belongs_to :owner, polymorphic: true
belongs_to :supervisor
end
问题是,当我使用以下代码创建Person :: Employee时,owner_type
列设置为不正确的值:
group = Group.create
=> #<Group id: 1, owner_id: nil, owner_type: nil ... >
group.update owner: Person::Employee.create
=> true
group
=> #<Group id: 1, owner_id: 1, owner_type: "Person" ... >
owner_type
应设置为"Person::Employee"
,但设置为"Person"
。
奇怪的是,在调用Group#owner
时,这似乎不会导致任何问题,但在创建如下关联时确实会导致问题:
应用程序/模型/ supervisor.rb:
class Supervisor < ActiveRecord::Base
has_many :groups
has_many :employees, through: :groups, source: :owner,
source_type: 'Person::Employee'
end
使用此类关联时,调用Supervisor#employees
将不会产生任何结果,因为它正在查询WHERE "groups"."owner_type" = 'People::Employees'
,但owner_type
设置为'People'
。
为什么此字段设置不正确以及可以采取哪些措施?
修改
根据this,owner_type
字段未设置不正确,但它按设计工作并将字段设置为 base的名称 STI模型。
问题似乎是has_many:through关联搜索Group
s并将owner_type
设置为模型的拥有名称,而不是 base 模型的名称。
设置正确查询has_many :employees, through: :group
条目的Person::Employee
关联的最佳方法是什么?
答案 0 :(得分:5)
您正在使用Rails 4,因此您可以在关联中设置范围。您的Supervisor
课程可能如下:
class Supervisor < ActiveRecord::Base
has_many :groups
has_many :employees, lambda {
where(type: 'Person::Employee')
}, through: :groups, source: :owner, source_type: 'Person'
end
然后你可以要求主管的员工如supervisor.employees
,它会生成如下查询:
SELECT "people".* FROM "people" INNER JOIN "groups" ON "people"."id" =
"groups"."owner_id" WHERE "people"."type" = 'Person::Employee' AND
"groups"."supervisor_id" = ? AND "groups"."owner_type" = 'Person'
[["supervisor_id", 1]]
这使您可以使用标准关联助手(例如build
),并且比编辑2更简单。
答案 1 :(得分:0)
我确实提出了这种解决方法,它添加了一个回调来为owner_type
设置正确的值:
class Group < ActiveRecord::Base
belongs_to :owner, polymorphic: true
before_validation :copy_owner_type
private
def copy_owner_type
self.owner_type = owner.type if owner
end
end
但是,我不知道这是否是最佳和/或最优雅的解决方案。
修改强>
在发现owner_type
字段假设要设置为基本STI模型后,我想出了以下方法来查询Person::Employee
条目Group
型号:
class Supervisor < ActiveRecord::Base
has_many :groups
def employees
Person::Employee.joins(:groups).where(
'people.type' => 'Person::Employee',
'groups.supervisor_id' => id
)
end
end
但是,这似乎并没有缓存其结果。
编辑2:
我提出了一个更优雅的解决方案,包括设置has_many:通过关联与基本模型关联,然后创建一个范围来仅查询继承的模型。
class Person < ActiveRecord::Base
scope :employees, -> { where type: 'Person::Employee' }
end
class Supervisor < ActiveRecord::Base
has_many :groups
has_many :people, through: :groups, source: :owner, source_type: 'Person'
end
有了这个,我可以致电Supervisor.take.people.employees
。