Rails 3 has_many :through + join table conditions / scoping

对着背影说爱祢 提交于 2019-12-03 05:17:45

问题


I'm working on an app that has the models User and Project, and User can be assigned to multiple Projects, via ProjectUser, with a role (e.g. Developer, Designer).

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

I can call @project.users and @user.projects, but since there are varying roles, I'd like to be a bit more specific with the relations. Ideally, I want to be able to do the following:

@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'

I currently have the following code:

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

But this only really does the fetching one-way, and doesn't give me much else - I can't build or assign or anything.

I'm attempting some more complex logic which I think might work, but would appreciate some pointers:

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

Any suggestions? Thanks!


回答1:


has_many accepts a block that can define/override methods for the association. This will allow you to create a custom method for <<. I've created a small example for you, you could create build in a similar fashion.

# 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

Now you can add a new developer to your your project with: @project.developers << @user as requested. @project.developers gives you all the developers.

If you have a lot of roles, it might be useful to create these has_many statements dynamically.

# 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

Looking back at everything above it doesn't seem like the rails way of doing things. Scoping this should make it possible to get the build and create commands working without redefining everything.

Hope this helps!




回答2:


It sounds like what you're looking for is a combination of RoR's single table inheritance and named scopes.

Take a look at the following article for a nice example about polymorphic associations. This should help you with achieving the following:

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

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

Scopes will give you a clean way to implement @user.development_projects but there may be more trickery required to get the << operator.




回答3:


Did you try using scopes yet? It doesn't let you do <<. But it simplifies querying.

Try:

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

You will be able to get all developers using: @project.developers



来源:https://stackoverflow.com/questions/7753162/rails-3-has-many-through-join-table-conditions-scoping

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!