#ruby-on-rails-3 #activerecord #arel
#ruby-on-rails-3 #activerecord #arel
Вопрос:
Я работаю над приложением, которое имеет модели User
и Project
, и User
может быть назначено нескольким Project
s через 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?
Есть предложения? Спасибо!
Ответ №1:
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
Оглядываясь назад на все вышесказанное, это не похоже на способ выполнения rails. Это должно позволить заставить команды сборки и создания работать без переопределения всего.
Надеюсь, это поможет!
Комментарии:
1. Спасибо за ваш ответ. Это решает мою проблему, но делает это не так, как я надеялся, — используя области видимости в
ProjectUsers
модели. Объявление:conditions => "project_users.role = '#{role}'"
кажется не очень рельсовым, так как мне хотелось бы вызвать что-то вроде:conditions => { :scope => :developer }
. Я все еще уверен, что это как-то возможно. В любом случае, я присуждаю вам награду за ваши усилия, хотя этот ответ не будет отмечен как правильный. Спасибо за ваш вклад!2. Спасибо. Я действительно ожидал, что
has_many :designers, :through => :project_users, :source => :user, :conditions => {:project_users => {:role => :designer}}
будет работать автоматически, но, по-видимому, хэши вложенных условий не ограничены с помощью build и create. Это было лучшее, что я мог придумать.3. что
proxy_owner
в приведенном выше? является ли эта ссылка другой стороной ассоциации, аналогичнойproxy_association
той, которую вы получаете с:extend
?4.
proxy_owner
относится к экземпляру (в данном случае)Project
объекта.
Ответ №2:
Похоже, то, что вы ищете, представляет собой комбинацию наследования одной таблицы 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
но для получения <<
оператора может потребоваться больше хитрости.
Ответ №3:
Вы уже пробовали использовать области? Это не позволяет вам делать <<. Но это упрощает выполнение запросов.
Попробуйте:
Project
scope :developers, lambda {
includes(:project_users).where("project_users.role = ?", "developer")
}
Вы сможете заставить всех разработчиков использовать: @project.developers