【问题标题】:"has_many :through" association through a polymorphic association with STI“has_many :through” 通过与 STI 的多态关联进行关联
【发布时间】:2014-06-23 09:27:07
【问题描述】:

我有两个使用people 表的模型:PersonPerson::Employee(继承自Person)。 people 表有一个 type 列。

还有另一个模型Group,它有一个称为:owner 的多态关联。 groups 表同时具有 owner_id 列和 owner_type 列。


app/models/person.rb:

class Person < ActiveRecord::Base
    has_one :group, as: :owner
end

app/models/person/employee.rb:

class Person::Employee < Person
end

app/models/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 时似乎不会导致任何问题,但在创建如下关联时却会导致问题:

app/models/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'

为什么这个字段设置不正确,可以做些什么?


编辑:

根据thisowner_type 字段没有设置不正确,但它按设计工作并将字段设置为 base 的名称 STI 模型。

问题似乎是 has_many :through 关联搜索 Groups 并将 owner_type 设置为模型的自己的名称,而不是 base模特的名字。

设置正确查询Person::Employee 条目的has_many :employees, through: :group 关联的最佳方法是什么?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 model polymorphic-associations


    【解决方案1】:

    您使用的是 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 稍微简单一些。

    【讨论】:

      【解决方案2】:

      我确实想出了这个解决方法,它添加了一个回调来为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模型后,我想出了以下方法通过Group模型查询Person::Employee条目:

      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 :through 关联以与基本模型关联,然后创建一个范围以仅查询继承的模型。

      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

      【讨论】:

      • 你测试了吗?拥有Person 真的会导致任何问题吗?
      • 我澄清了我的问题,但是在执行指向 belongs_to :owner 关系的 has_many :through 关系时会出现问题。我已经测试了这段代码,到目前为止它似乎运行良好。
      • 好的,我问这个问题是因为这个问题,这基本上与你所做的相反:stackoverflow.com/questions/9628610/…
      • 好的,这说明owner_type 应该是base 模型的名称。这让我觉得我的解决方法有点笨拙。问题是,我将如何只搜索模型 Person::EmployeeownerGroups?
      猜你喜欢
      • 2011-09-28
      • 2014-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-13
      • 2016-03-20
      • 2015-12-17
      • 1970-01-01
      相关资源
      最近更新 更多