Rails STI与子类的关联

Tom*_*sey 7 sql activerecord ruby-on-rails sti

在使用STI时,从使用rails 3的has_many关联中获取集合时,我遇到了一些奇怪的行为.我有:

class Branch < ActiveRecord::Base
   has_many :employees, class_name: 'User::Employee'
   has_many :admins, class_name: 'User::BranchAdmin'
end

class User < ActiveRecord::Base
end

class User::Employee < User
  belongs_to :branch
end

class User::BranchAdmin < User::Employee
end
Run Code Online (Sandbox Code Playgroud)

期望的行为是branch.employees返回所有员工,包括分支管理员.分支管理员在访问它们时似乎只在此集合下"加载" branch.admins,这是从控制台输出的:

Branch.first.employees.count
=> 2

Branch.first.admins.count
=> 1

Branch.first.employees.count
=> 3
Run Code Online (Sandbox Code Playgroud)

这可以在生成的SQL中看到,第一次:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee') AND "users"."branch_id" = 1
Run Code Online (Sandbox Code Playgroud)

第二次:

SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = 1
Run Code Online (Sandbox Code Playgroud)

我只需指定以下内容即可解决此问题:

class Branch < ActiveRecord::Base
   has_many :employees, class_name: 'User'
   has_many :admins, class_name: 'User::BranchAdmin'
end
Run Code Online (Sandbox Code Playgroud)

因为它们都是从他们的branch_id中找到的,但是这会在控制器中产生问题,如果我想这样做,branch.employees.build那么类将默认为User,我必须在某处修改类型列.我现在已经解决了这个问题:

  has_many :employees, class_name: 'User::Employee', 
    finder_sql: Proc.new{
      %Q(SELECT users.* FROM users WHERE users.type IN          ('User::Employee','User::BranchAdmin') AND users.branch_id = #{id})
    },
    counter_sql: Proc.new{
      %Q(SELECT COUNT(*) FROM "users" WHERE "users"."type" IN ('User::Employee', 'User::BranchAdmin') AND "users"."branch_id" = #{id})
    }
Run Code Online (Sandbox Code Playgroud)

但如果可能的话,我真的想避免这种情况.任何人,任何想法?

编辑:

该finder_sql和counter_sql还没有真正解决了这个问题对我来说,因为它似乎是家长协会不使用这一点,所以organisation.employeeshas_many :employees, through: :branches将再次只包括User::Employee在选择类.

Sha*_*nce 20

基本上,该问题仅存在于根据需要加载类的开发环境中.(在生产中,类已加载并保持可用.)

问题来自于解释器还没有看到,这AdminsEmployee你第一次运行Employee.find等调用时的类型.

(请注意以后使用IN ('User::Employee', 'User::BranchAdmin'))

每次使用深度超过一级的模型类时都会发生这种情况,但仅限于开发模式.

子类始终自动加载其父层次结构.基类不会自动加载他们的子级别.

哈克修复:

您可以通过显式要求基类rb文件中的所有子类来强制在dev模式下执行正确的行为.

  • 这是一个很好的收获,谢谢.模型结构实际上已经改变了,所以问题消失了,但我认为我甚至不认为它是环境的影响! (2认同)