Rails 3 нетерпеливая загрузка с условиями-как получить доступ к нетерпеливо загруженным данным?


У меня есть много случаев в моем приложении, когда пользователь имеет не более одного объекта (скажем, "описание") в пределах его ассоциации с другим объектом ("группой").

Например:

class User < ActiveRecord::Base
  has_many :descriptions
  has_many :groups

class Group < ActiveRecord::Base
  has_many :users
  has_many :descriptions

class Description < ActiveRecord::Base
  belongs_to :user
  belongs_to :group

Если бы я хотел отобразить всех пользователей в определенной группе и включить их соответствующие описания, я мог бы сделать что-то такое:

#users model
def description_for(group_id)
  descriptions.find_by_group_id(group_id)
end

#view
@group.users.each do |user|
   user.name
   user.description_for(@group.id).content
Но это порождает огромное количество описательных запросов. Я пробовал использовать соединения:
#controller
@group = Group.find(params[:id], :joins => [{:users => :descriptions}], :conditions => ["descriptions.group_id = ?", params[:id]])

, но так как я до сих пор вызывающего пользователя.description_for(@группа.идентификатор) это не помогает с загрузкой страницы.

Обновление: образец сгенерированного SQL

Rendered users/_title.html.haml (1.6ms)
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1
User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 7 LIMIT 1
CACHE (0.0ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1
Description Load (0.1ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 7 AND "descriptions"."group_id" = 28 LIMIT 1
CACHE (0.0ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 7 AND "descriptions"."group_id" = 28 LIMIT 1
Rendered users/_title.html.haml (1.7ms)
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1
User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 51 LIMIT 1
CACHE (0.0ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1
Description Load (0.1ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 51 AND "descriptions"."group_id" = 28 LIMIT 1
CACHE (0.0ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 51 AND "descriptions"."group_id" = 28 LIMIT 1
Rendered users/_title.html.haml (1.8ms)
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1
User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 5 LIMIT 1
CACHE (0.0ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1
Description Load (0.1ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 5 AND "descriptions"."group_id" = 28 LIMIT 1
CACHE (0.0ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 5 AND "descriptions"."group_id" = 28 LIMIT 1
Rendered users/_title.html.haml (1.7ms)
CACHE (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 37 LIMIT 1
User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 52 LIMIT 1
CACHE (0.0ms)  SELECT "groups".* FROM "groups" WHERE "groups"."id" = 28 LIMIT 1
Description Load (0.2ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 52 AND "descriptions"."group_id" = 28 LIMIT 1
CACHE (0.0ms)  SELECT "descriptions".* FROM "descriptions" WHERE "descriptions"."target_id" = 52 AND "descriptions"."group_id" = 28 LIMIT 1
Rendered users/_title.html.haml (1.7ms)
2 4

2 ответа:

Верно, я думаю, что на самом деле вам не нужно предложение joins в rails 3. Если вы используете include и where, Arel сделает тяжелую работу за вас.

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

В модели / пользователь.РБ:

scope :with_group_and descriptions, lambda { |group_id| includes(:groups, :descriptions).where(:groups => { :id => group_id }, :descriptions => { :group_id => group_id }) }

Затем в своем контроллере вы вызываете:

@users = User.with_group_and_descriptions(params[:id])

Наконец, в представлении вы можете сделать:

@users.each do |user|
  user.name
  user.descriptions.each do |desc|
    desc.content
# or
@users.each do |user|
  user.name
  user.descriptions[0].content

Если Я правильно понял, что это должно сделать только 2 db вызовов. Во-первых, чтобы получить список user_ids, а во-вторых, чтобы получить данные пользователя, группы и описания, и даже если вы вызываете метод описания объекта пользователя, который обычно содержит все описания (а не только для определенной группы), потому что вы уже заполнили ассоциативные рельсы, они не будут снова захватывать все ассоциации при вызове user.descriptions, вместо этого он будет просто перечислять те, которые вы вытащили из БД, использующая описания.предложение group_id where. Вызов user.descriptions(true) однако вызовет перезагрузку описаний, приводящую к тому, что он возвращает массив всех описательных ассоциаций для пользователя.

Взгляните на include - это указывает на ассоциацию, которая должна быть загружена с нетерпением.