2015-12-09 2 views
1

Надеюсь, я могу это ясно объяснить.Rails: предварительная загрузка has_many через отношение в контроллере

  • A has_many уведомления пользователя и has_many фильмы через уведомлений. Фильм has_many трейлеры.

  • Уведомления содержат 3 столбца: user_id, movie_id и веб-сайт.

  • на прицепы # индекса, я рендеринг @trailers и каждый @trailer оказывает -х АЯКС notification_forms (каждая форма является лишь одной кнопкой, одна для каждого варианта «сайта». Когда пользователь нажимает на эту кнопку, запрос Ajax отправляется, чтобы создать новую запись уведомления)

  • я уследить из которых была нажата кнопка (глядя вверх, чтобы увидеть, если уведомление существует для этого пользователя/фильма)

меня беспокоит то, что потому что я только предварительно загружаю @trailers, в настоящее время я делаю 3 новых вызова БД на каждой итерации трейлера.

_notification_form.html.erb (вызывается 3 раза на каждом прицепе)

<% if trailer.movie.tracked?(current_user, "netflix") %> 
    ## do some css stuff on the button to show notification already set 

movie.rb:

def tracked?(user, website) 
    user.notifications.where(movie_id: self.id, website: website).first 
    end 

Таким образом, каждый раз, когда трейлер частичное визуализируется, я делаю 3 Вызов БД. Каков правильный способ справиться с этой ситуацией?

ответ

1

Поскольку вы только проверять состояние current_user и каждый Trailer знает его movie_id, вы можете просто загрузить User 's Notification с и проверить их на Trailer S' movie_id с:

tracked = current_user.notifications 
tracked.detect { |n| n.movie_id == trailer.movie_id && n.website == 'netflix' } 

Если User может иметь тонну из Notification с, вы можете ограничить набор только к Notification с для Trailer с на странице вы показываете:

tracked = current_user.notifications.where(movie_id: trailers.map(&:movie_id)) 

Загрузите это, а затем просто проверьте его, когда вы решаете, как отображать ваши кнопки. Вы можете сделать его еще более эффективным, создав Set, содержащий пары [movie_id, website], которые будут выполнять поиск в постоянное время.

tracked = Set.new(
    current_user.nofications 
    .where(movie_id: trailers.map(&:movie_id)) 
    .pluck(:movie_id, :website) 
) 
tracked.include?([trailer.movie_id, 'netflix']) 

Эта стратегия будет быстрой для вас, поскольку она точно соответствует данным, которые вы проверяете. Если вам нужно что-то более гибкое, которое все еще сводит к минимуму запросы к базе данных, посмотрите на загруженные ассоциации includes, preload и eager_load. 3 ways to do eager loading (preloading) in Rails 3 & 4 имеет отличное описание различий между этими методами. Все они работают во время загрузки начального отношения (то есть, если вы еще ничего не загрузили), но когда вы уже загрузили часть данных (например, у вас есть Trailer s, но не Movies s или Notification s еще), вы можете использовать ActiveRecord::Associations::Preloader, чтобы эффективно загрузить остальную часть.

+0

Спасибо! Это потрясающе –

Смежные вопросы