ActiveRecord запрос по трем моделям? - PullRequest
3 голосов
/ 21 января 2011

У меня есть три модели:

class User < ActiveRecord::Base
 has_many :projects, :through => :permissions

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project
 belongs_to :role

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

С помощью вышеперечисленного очень легко получить всех пользователей Проекта: @project.users

Но я хочу получить что-то вроде этого: получить всех пользователей во всех проектах пользователя.

Так, если у пользователя есть 3 проекта, в каждом из которых по 5 пользователей. Я хочу отправить запрос всем 15 пользователям во всех группах пользователей.

Я пытаюсь это сделать.

 current_user.projects.users 

но Rails не очень нравится. current_user.projects прекрасно работает, но не пользователи.

Предложения? Идеи? спасибо!

ОБНОВЛЕННЫЙ КОД 3 на основе комментариев лапши

  scope :suggestedContacts, lambda { |user|
    users_from_projects = user.projects.reduce([]) {|all_users,prj|
      all_users + prj.users
    }.uniq
  }

ОШИБКИ:

NoMethodError (неопределенный метод "includes_values" для #):

Ответы [ 6 ]

3 голосов
/ 21 марта 2011

Мои два решения:

  • clean , простой метакод стандартных рельсов, нет пользовательских искателей
  • эффективная , выборка в одном запросе SQL, без проблем N + 1
  • соответствует , что означает, что вы все еще можете строить, создавать и т. Д. Для отношения

1

Вы можете связать свои отношения в классе пользователя.
Поскольку rails 3.0.x еще не поддерживает вложенный has_many_through, вы можете использовать этот плагин до rails 3.1

class User < ActiveRecord::Base
 has_many :permissions
 has_many :projects, :through => :permissions
 has_many :users_in_projects, :through => :projects, :source => :user # chain the relation

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

current_user.users_in_projects

2.

Другим способом было бы увеличить нагрузку и уменьшить ее (как уже было описано в других ответах, но я сделаю это более явным).
Это больше работы, меньше зависимостей.

class User < ActiveRecord::Base
 has_many :permissions
 has_many :projects, :through => :permissions, :include => :users # eager load users

class Permission < ActiveRecord::Base
 belongs_to :user
 belongs_to :project

class Project < ActiveRecord::Base
 has_many :users, :through => :permissions

current_user.projects.map(&:users).reduce(&:+).uniq_by(&:id) 
# returns users in current_user's projects, one query, some computations
2 голосов
/ 21 января 2011

@ noodl имеет правильную идею, но да, это вызывает "некоторые" накладные расходы. Конечно, если вы не говорите о миллионах проектов и разрешений, вы не заметите значительного улучшения благодаря оптимизации. Несмотря на это, вот альтернативный подход, который в теории, хотя и не очень, дает вам то, что вы хотите "эффективно" (при условии, что ваши таблицы проиндексированы правильно).

class User

  has_many :project_users, :class_name => "User", :finder_sql => 'select u.* from users u join permissions pp on pp.user_id=u.id join projects p on pp.project_id=p.id where project_id in (select project_id from permissions pp2 where user_id=#{id}) and pp.user_id != #{id} group by u.id'

end

Это не сфера, но позволяет вам сказать:

user = User.find(1)
=> #<User id: 1, name: "Stephen">
user.project_users
=> [#<User id: 2, name: "Kathleen">, #<User id: 3, name: "Anne">]
1 голос
/ 21 января 2011

Поскольку потоки комментариев становятся немного трудоемкими, я попытаюсь объяснить, как бы я решил это напрямую.

users = current_user.projects.map(&:users).flatten.uniq
0 голосов
/ 22 января 2011

К вашему классу User добавьте:

 has_many :users, :through => :permissions

Так это будет выглядеть так:

 class User < ActiveRecord::Base
   has_many :projects, :through => :permissions
   has_many :users, :through => :permissions
0 голосов
/ 21 января 2011

Вам нужно нетерпеливо загружать свои ассоциации

class User < ActiveRecord::Base
  has_many :projects, :through => :permissions, :include => :users
  has_many :permissions, :include => {:project => :users}
  …
end
0 голосов
/ 21 января 2011

Поскольку проекты являются агрегатами, они будут возвращать перечисляемые объекты. Вы можете перебирать их, используя (например) каждый:

current_user.projects.each {|prj|
  prj.users.each{|usr|
    #do stuff
  }
}

Если вы хотите собрать их в одно перечисляемое (и избавиться от повторов), тогда

users_from_projects = current_user.projects.reduce([]) {|all_users,prj|
  all_users = all_users + prj.users
}.uniq

РЕДАКТИРОВАТЬ, чтобы исправить, спасибо за указание на лапшу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...