Фильтрация дочерних объектов в has_many: через отношения в Rails 3
Привет,
У меня есть приложение, в котором Companies
и Users
должны принадлежать друг другу с помощью модели CompanyMembership
, которая содержит дополнительную информацию о членстве (в частности, является ли пользователь администратором компании, через логическое значение admin
). Простая версия кода:
class CompanyMembership < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class Company < ActiveRecord::Base
has_many :company_memberships
has_many :users, :through => :company_memberships
end
class User < ActiveRecord::Base
has_many :company_memberships
has_many :companies, :through => :company_memberships
end
Конечно, это упрощает получение всех членов компании через company.users.all
и др. Тем не менее, я пытаюсь получить список всех пользователей в компании, которые являются администраторами этой компании (а также проверить, является ли пользователь администратором данной компании). Моим первым решением было следующее в company.rb
:
def admins
company_memberships.where(:admin => true).collect do |membership|
membership.user
end
end
def is_admin?(user)
admins.include? user
end
В то время как это работает, что-то чувствует себя неэффективным (он перебирает каждое членство, каждый раз выполняет SQL, или это отношение более умное, чем это?), и я не уверен, есть ли лучший способ сделать это (возможно, с использованием областей или новых объектов Relation
, которые использует Rails 3?).
Приветствуются любые советы по наилучшему методу (предпочтительно, используя лучшие практики Rails 3)!
Ответы
Ответ 1
Я считаю, что я ошибался, указав условия company_memberships
вместо users
, что я и хотел (список users
, а не список CompanyMemberships
). Решение, которое, я думаю, я искал, это:
users.where(:company_memberships => {:admin => true})
который генерирует следующий SQL (для компании с идентификатором 1):
SELECT "users".* FROM "users"
INNER JOIN "company_memberships"
ON "users".id = "company_memberships".user_id
WHERE (("company_memberships".company_id = 1))
AND ("company_memberships"."admin" = 't')
Я еще не уверен, если мне это понадобится, но метод includes()
будет выполнять загружаемую загрузку, чтобы при необходимости уменьшить количество SQL-запросов:
Активная запись позволяет указать продвигать все ассоциации, которые будет загружен. Это возможно указав метод includes
вызов Model.find
. С включением, Активная запись гарантирует, что все заданные ассоциации загружаются используя минимально возможное количество queries.queries. RoR Guides: запрос ActiveRecord
(Я по-прежнему открыт для любых предложений от тех, кто считает, что это не лучший/самый эффективный/правильный способ сделать это.)
Ответ 2
Еще более чистым способом было бы добавить связь с вашей моделью компании, примерно так:
has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true}
Вам может понадобиться вставить rails doc, чтобы получить точный синтаксис.
Вам не нужно: включать, если у вас нет других классов, связанных с: пользователем, который вы можете ссылаться в своем представлении.
Ответ 3
Как насчет чего-то вроде этого:
Company.find(:id).company_memberships.where(:admin => true).joins(:user)