Rails 3 has_many:通过+连接表条件/范围

时间:2011-10-13 11:06:16

标签: ruby-on-rails-3 activerecord arel

我正在开发一个包含模型UserProject的应用,User可以通过Project分配给多个ProjectUser ,有角色(例如开发人员,设计师)。

Project
  has_many :project_users
  has_many :users, :through => :project_users

User
  has_many :project_users
  has_many :projects, :through => :project_users

ProjectUser (user_id, project_id, role)
  belongs_to :user
  belongs_to :project

我可以致电@project.users@user.projects,但由于角色各不相同,我希望对这些关系更加具体。理想情况下,我希望能够做到以下几点:

@project.developers
  # returns @project.users, but only where ProjectUser.role = 'Developer'

@project.designers << @user
  # creates a ProjectUser for @project, @user with role 'Designer'

@user.development_projects
  # returns projects where @user is assigned as a 'Developer'

@user.design_projects << @project
  # creates a ProjectUser for @project, @user with role 'Designer'

我目前有以下代码:

has_many :developers, :through => :project_users, :source => :user,
                      :class_name => "User",
                      :conditions => ['project_users.role = ?','Developer']

但这只是单向提取,并没有给我太多其他的东西 - 我无法建立或分配任何东西。

我正在尝试一些我认为可能有用的更复杂的逻辑,但我会欣赏一些指示:

has_many :developer_assignments, :source => :project_user,
                                 :conditions => { :role => 'Developer' }
has_many :developers, :through => :developer_assignments # class_name?

有什么建议吗?谢谢!

3 个答案:

答案 0 :(得分:12)

has_many接受一个可以定义/覆盖关联方法的块。这将允许您为<<创建自定义方法。我为你创建了一个小例子,你可以用类似的方式创建构建。

# Project.rb
has_many :developers, :through => :project_users, :source => :user,
         :conditions => "project_users.role = 'developer'" do
         def <<(developer)
           proxy_owner.project_users.create(:role => 'developer', :user => developer)
         end
       end

现在,您可以根据要求使用@project.developers << @user向项目中添加新的开发人员。 @project.developers为您提供所有开发人员。

如果您有很多角色,那么动态创建这些has_many语句可能会很有用。

# Project.rb
ROLES = ['developer','contractor']

ROLES.each do |role|         
  self.class_eval <<-eos
    has_many :#{role.downcase}s, :through => :project_users, :source => :user,
           :conditions => "project_users.role = '#{role}'" do
             def <<(user)
               proxy_owner.project_users.create(:role => '#{role}', :user => user)
             end
           end
  eos
end

回顾上面的所有内容似乎不是做事的方式。确定范围可以使构建和创建命令成为可能,而无需重新定义所有内容。

希望这有帮助!

答案 1 :(得分:1)

听起来你正在寻找的是RoR single table inheritancenamed scopes的组合。

请查看以下article以获取有关多态关联的一个很好的示例。这可以帮助您实现以下目标:

@project.developers
  # returns @project.users, but only where ProjectUser.role = 'Developer'

@project.designers << @user
  # creates a ProjectUser for @project, @user with role 'Designer'

范围将为您提供实施@user.development_projects的简洁方法,但获取<<运算符可能需要更多技巧。

答案 2 :(得分:0)

您尝试使用scopes了吗?它不会让你做&lt;&lt;但它简化了查询。

<强>尝试:

Project
  scope :developers, lambda {
    includes(:project_users).where("project_users.role = ?", "developer")
  }

您将能够让所有开发人员使用:@project.developers