从具有多态关联的一个表中检索行

时间:2011-08-23 14:12:22

标签: ruby-on-rails ruby activerecord join polymorphic-associations

我有一个模型与其他三个表有多态关联:

class User < ActiveRecord::Base
    belongs_to :authenticatable, :polymorphic => true


# == Schema Information
#
# Table name: employees
#
#  id                     :integer(4)      not null, primary key
#  ...
#  school_id              :integer(4)

class Guardian < ActiveRecord::Base
    has_one :user, :as => :authenticatable
    belongs_to :school


# == Schema Information
#
# Table name: guardians
#
#  id                     :integer(4)      not null, primary key
#  ...
#  school_id              :integer(4)

class Guardian < ActiveRecord::Base
    has_one :user, :as => :authenticatable
    belongs_to :school

# == Schema Information
#
# Table name: students
#
#  id                     :integer(4)      not null, primary key
#  ...
#  school_id              :integer(4)

class Student < ActiveRecord::Base
    has_one :user, :as => :authenticatable
    belongs_to :school

如您所见,最后3个模型属于“学校”模型,因此有一个名为school_id的列。我想从user检索所有行,使其对应的可验证的school_id等于某个值。为了澄清,我想做这样的事情:

User.joins(:authenticatable).where(:school_id => some_value)

实际上,这将导致

ActiveRecord::EagerLoadPolymorphicError

最后一点,我设法查找了一些文档,这些文档表明在多态关联中使用include应该有效,例如:

User.find(:all, :include => :authenticatable) (This works)

但是,如果我这样做:

User.find(:all, :include => :authenticatable).where(:school_id => some_value)

它会破坏rails,因为User.find(...)返回Array并且没有为该类定义where方法。

我已经尝试了其他一些选择,并没有找到实现我想要的方法。你能帮助我吗?谢谢!

2 个答案:

答案 0 :(得分:1)

您可以尝试使用joins语句中的SQL查询来解决它:

Model1.joins("INNER JOIN model2 ON model2.id = model1.polymorphizm_id INNER JOIN model3 ON model3.id = model1.polymorphizm_id INNER JOIN model4 ON model4.id = model1.polymorphizm_id").where("model2.column_id = ... or model3.column_id = ... or model4.column_id = ...")

我实际上并没有尝试过,而是polimorphic assoc。为模型添加了2列:xxx_typexxx_id。他们用来处理assoc。有多种型号。

答案 1 :(得分:0)

你可以通过子查询来获得好处,也许可以得到一些有用的东西:

User.where(authenticable: Guardian.where(school_id: some_value))

如果您正在使用Rails 5,他们会将.or添加到ActiveRecord,因此您可以像以下一样加入其中:

User.where(authenticable: Guardian.where(school_id: some_value))
    .or.where(authenticable: Student.where(school_id: some_value))

merge是另一种可能产生一些结果的方法:

User.all.merge(Guardian.where(school_id: some_value))

我不太熟悉查询多态表,因此您的里程可能会因上述情况而有所不同。最糟糕的情况是,您最终可能需要执行多个查询来保留范围:

user_ids = Guardian.joins(:user).select('users.id').where(school_id: some_value).pluck(:user_id) +
           Student.joins(:user).select('users.id').where(school_id: some_value).pluck(:user_id)
users = User.where(id: user_ids)

您现在可能会遇到架构,但如果您可以随意更改它,看起来您可能会更好地使用单表继承关系:

class User < ActiveRecord::Base
  belongs_to :school
end

class Student < User
end

class Guardian < User
end

然后你只需说:

User.where(school_id: some_value)

我发现多态关系比它们在很多情况下的价值更加麻烦,因为查询和批量更新这样的事情是多么困难,以及无法使用外键限制。通过一些额外的思考,通常情况下合理的数据模型在没有它们的情况下运行良好。