Rails 3 Eager Загрузка с условиями - как получить доступ к загруженным данным? - PullRequest
4 голосов
/ 26 июля 2011

В моем приложении много случаев, когда у пользователя есть не более одного объекта (скажем, «Описание») в его ассоциации с другим объектом («Группой»).

Например:

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]])

Но так как я до сих пор звоню user.description_for (@ group.id), это не помогает при загрузке страницы.

ОБНОВЛЕНИЕ: Образец сгенерированного 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 ]

1 голос
/ 16 августа 2011

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

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

в моделях / user.rb:

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 дБ. Один для получения списка user_ids, второй для получения данных о пользователе, группе и описании, и даже при том, что вы вызываете метод описаний объекта пользователя, который обычно содержит все описания (а не только для определенной группы). ), поскольку вы уже заполнили рельсы ассоциаций, они не будут снова захватывать все ассоциации при вызове user.descriptions, а просто перечислят те, которые вы извлекли из БД, используя describ.group_id пункт. Однако вызов user.descriptions(true) приведет к перезагрузке описаний, что приведет к возвращению массива всех ассоциаций описания для пользователя.

0 голосов
/ 26 июля 2011

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

...