Rails 一个有点复杂的多对多映射

hexawing · 2012年07月23日 · 最后由 jimrokliu 回复于 2012年08月01日 · 4237 次阅读

大致模型结构是这样:用户 (多对多) 部门 (多对多) 部门经理。因为经理也是用户,所以数据库表是这样:

用户 (User) 部门 (Department) 部门经理 (User) 用户 | 部门关联 (DepartmentUser) 部门 | 部门经理关联 (DepartmentManager)

我想实现的是:查找用户 A 所在所有部门的经理们

我自己也摸索了一下,各种多对多的模型已经弄好,如下:

user.rb↓

class User < ActiveRecord::Base  
  #作为用户所属的部门们  
  has_many :departments_users  
  has_many :departments, :through => :departments_users  

  #作为经理掌管的部门们  
  has_many :departments_managers  
  has_many :directs, :through => :departments_managers, :source => :department  
end  

department.rb↓

class Department < ActiveRecord::Base  
  #部门的用户们  
  has_many :departments_users  
  has_many :users, :through => :departments_users  

  #部门的经理们  
  has_many :departments_managers  
  has_many :managers, :through => :departments_managers, :source => :user  
end  

departments_manager.rb↓


class DepartmentsManager < ActiveRecord::Base  
  belongs_to :department  
  belongs_to :user  
end  

departments_user.rb↓


class DepartmentsUser < ActiveRecord::Base  
  belongs_to :department  
  belongs_to :user  
end  

然后我假定的数据是这样:

数据结构示意

自己用类似 User.find(1).departments 之类的查询试了几个都无误。然后我想查“用户 3 所在的部门的经理们”,就出现了问题:

User.where("users.id = 3").joins(:departments => :managers).map{|p| p.id}
=> [3, 3, 3]

我期望得到的是 [1,3,5] 才对。我怀疑是 where 条件里的 users.id = 3 写得不对,但这里不写 users.id 又写什么呢……

模型设计的好像有点怪,我猜可以这样。

User.find_by_id(3). departments.collect {|x| x. managers}

#1 楼 @jimrokliu 这么写可以得到,但还是要对结果稍作变换。是我的模型设计不对是吗?那请教一下该如何写比较正确呢?

class User < ActiveRecord::Base  
      #作为用户所属的部门们  
      has_many :departments_users  
      has_many :departments, :through => :departments_users  

      #all managers   
      has_many :directs, :class_name => "DepartmentsManager",:through => :departments, :source => :user  
    end

User.find_by_id(3).directs

这样不知道行不行。

#3 楼 @jimrokliu 不行啊老大,报错:

[1] pry(main)> u=User.find(2)
=> #<User id: 2, reg_name: "marian", name: "王小丽", name_en: "Marian Wang", email: "[email protected]", hashed_password: nil, salt: nil, status: 1, extension: "814", mobile: nil, qq: nil, msn: nil, created_at: "2012-07-28 04:01:17", updated_at: "2012-07-28 04:01:17">
[2] pry(main)> u.directs
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :user in model Department. Try 'has_many :directs, :through => :departments, :source => <name>'. Is it one of :departments_users, :users, :departments_managers, or :managers?
from /home/terry/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.0/lib/active_record/reflection.rb:509:in `check_validity!'

这个模型不会是中国特色吧。一个人能隶属多个部门,并且部门有多个领导。

#5 楼 @xds2000 呃,你说对了……有办法解决吗………………

class User < ActiveRecord::Base  
      #作为用户所属的部门们  
      has_many :departments_users  
      has_many :departments, :through => :departments_users  

      #all managers   
      has_many : departments_managers,:through => :departments
    end

User.find_by_id(3).departments_managers.map {|x| x.user}

不知道这样行不行

#4 楼 @hexawing 你这个错误信息很明显啊

ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :user in model Department. Try 'has_many :directs, :through => :departments, :source => <name>'. Is it one of :departments_users, :users, :departments_managers, or :managers?

has many through 要加 source 的,标识你的 sql 最终是从哪个表里面获取数据。

7 楼可行。

#7 楼 @jimrokliu 确实可行,虽然数据取出来是 [1,3,3,5] 这样子的……我 uniq 一下好了。我再仔细琢磨琢磨原理,大感谢!

我最近的项目中有类似的需求 我的做法是通过角色控制用户和经理因为要考虑到用户某天升级成为经理的可能性,而特殊字段则通过 profile 实现

#7 楼 @jimrokliu 我实在是太愚钝了,想自己加一个“departments_users”,写了半天也没成功,说我关联太复杂……

class User < ActiveRecord::Base  
  #作为用户所属的部门们  
  has_many :departments_users  
  has_many :departments, :through => :departments_users  

  #作为用户的部门经理们
  has_many :departments_managers,:through => :departments

  #作为经理管辖的用户们,不过似乎不是这么写的=_=
  has_many :departments_users, :through => :departments
end
class Department < ActiveRecord::Base  
  #部门的用户们  
  has_many :departments_users  
  has_many :users, :through => :departments_users, :source => :user

  #部门的经理们  
  has_many :departments_managers  
  has_many :managers, :through => :departments_managers, :source => :user  
end
class DepartmentsManager < ActiveRecord::Base  
  belongs_to :department  
  belongs_to :user  
end  
class DepartmentsUser < ActiveRecord::Base  
  belongs_to :department  
  belongs_to :user  
end

然后报错:

[1] pry(main)> u=User.find(2)
=> #<User id: 2, reg_name: "marian", name: "王小丽", name_en: "Marian Wang", email: "[email protected]", hashed_password: nil, salt: nil, status: 1, extension: "814", mobile: nil, qq: nil, msn: nil, created_at: "2012-07-28 04:01:17", updated_at: "2012-07-28 04:01:17">
[2] pry(main)> u.departments_managers
SystemStackError: stack level too deep
from /home/terry/.rvm/gems/ruby-1.9.3-p194/gems/pry-0.9.9.6/lib/pry/pry_instance.rb:249

不能两个关联 through 同一个类吗?这个 through 和 source,哪里有详细的讲解啊,实在是不懂了……

"stack level too deep" 调用层级太多了吧。是不是循环调用了。

不能两个关联 through 同一个类吗?这个 through 和 source,哪里有详细的讲解啊,实在是不懂了……去读官方文档,长了点,耐心读完

需要 登录 后方可回复, 如果你还没有账号请 注册新账号