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 ответа:
Верно, я думаю, что на самом деле вам не нужно предложение 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
- это указывает на ассоциацию, которая должна быть загружена с нетерпением.
- Railscasts #181: Include vs Joins (или ASCIIcast version)
- Ruby on Rails направляющие - см. раздел 4.1.2.7