Rails 3 has_many: сквозная + объединить условия таблицы / область видимости - PullRequest
9 голосов
/ 13 октября 2011

Я работаю над приложением, которое имеет модели User и Project, и 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 ]

12 голосов
/ 05 ноября 2011

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

Оглядываясь назад на все выше, это не похоже на путь рельсов ведения дел. Это позволяет сделать так, чтобы команды build и create работали без переопределения всего.

Надеюсь, это поможет!

1 голос
/ 01 ноября 2011

Звучит так, будто вы ищете комбинацию RoR и именованных областей .

Взгляните на следующую статью для хорошего примера полиморфных ассоциаций. Это должно помочь вам в достижении следующего:

@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, но для получения оператора << может потребоваться дополнительная хитрость.

0 голосов
/ 06 ноября 2011

Вы уже пробовали использовать области ? Это не позволяет вам сделать <<. Но это упрощает запросы. </p>

Попытка:

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

Вы сможете получить всех разработчиков, используя: @project.developers

...