簡體   English   中英

Rails:通過控制器中的關系預加載has_many

[英]Rails: preloading a has_many through relation in the controller

我希望我能清楚地解釋這一點。

  • 用戶通過通知has_many通知和has_many電影。 電影有很多預告片。

  • 通知包含3列:user_id,movie_id和網站。

  • 在trailers#index上,我正在渲染@trailers,每個@trailer都渲染3個ajax notification_forms(每個表單只是一個按鈕,每個“網站”選項一個。當用戶單擊此按鈕時,將發送ajax請求來創建一個新的通知記錄)

  • 我跟蹤已按下的按鈕(通過查找是否存在該用戶/電影的通知)

我擔心的是,因為我只預加載了@trailers,所以我當前在每個預告片迭代中進行3個新的DB調用。

_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次DB調用。 處理這種情況的正確方法是什么?

由於您只檢查current_user的狀態,並且每個Trailer知道它的movie_id ,因此您只需加載UserNotification並檢查它們的Trailermovie_id

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

如果一個User可能有一Notification S,你可以限制設置為僅Notification S為Trailer你顯示在頁面上S:

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

加載它,然后在決定如何呈現按鈕時僅對其進行檢查。 您可以通過創建包含[movie_id, website]對的Set提高效率,該Set將在固定時間內進行查找。

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

該策略將完全針對您要檢查的數據量身定制,因此對您來說將很快。 如果您需要更靈活的方法仍然可以最大程度地減少數據庫查詢,請查看includespreloadeager_load渴望加載的關聯。 Rails 3和4中3種進行緊急加載(預加載)的方法很好地描述了這些方法之間的差異。 這些都在初始關系加載期間(即,如果您還沒有加載任何東西)工作,但是當您已經加載了部分數據時(例如,您擁有Trailer但沒有MoviesNotification ),您可以使用ActiveRecord :: Associations :: Preloader有效地加載其余部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM