Reputation: 57
class Model RunningTracks < ActiveRecord::Base
has_many :achievements
has_many :runners, through: :achievements
class Model Runner < ActiveRecord::Base
has_many :achievements
has_many :running_tracks, through: :achievements
class Model Achievement < ActiveRecord::Base
belongs_to :runner
belongs_to :running_tracks
On the HomePage there is an each do
list to display all RunningTracks. Next to the track we display a button to mark it as run. An eager loading solution is an includes
and references
(Rails 4) in the query in the controller:
class RunningTracksController
def index
@running_tracks = RunningTracks.includes(:achievements).references(:achievements)
end
To improve the usability, the button next to each record should not be visible if it had been marked. (will be refactored in an helper method)
@running_tracks.each do |track|
.
- unless track.achievements.map(&:user_id).include? current_user.id
= button_to "done", running_track_mark_as_done_path(track)
This works...BUT... as the database gets more records it will query a lot of achievements, therefore a custom association and an includes
query for only the achievements of the current_user
(Devise) should be the solution.
class Model RunningTracks < ActiveRecord::Base
.
has_many :achieved_runns, -> { where user_id: current_user.id }, class_name "Achievement"
But because current_user is not accessible in the model, this does not work. I don't think its possible to pass a variable to the hay_many association, is it?
Is there a more efficient solution to do an eager loading of all RunningTracks including only the Achievements by the current user? Thank you for your valuable time!
Upvotes: 2
Views: 1036
Reputation: 4943
The following is ugly and breaks the ActiveRecord metaphor, but would give you very fast lookups as the DB grew in size. Maybe someone else can chime in with a way to keep this within the eager-loading paradigm.
class RunningTrack
# Returns a Set (for fast lookups) of track IDs that have at least one achievement for the provided user
def self.running_track_ids_for_achievements_for_user(user_id)
Set.new Achievement.where(:user_id => user_id).pluck(:running_track_id)
end
end
Then in your controller:
class RunningTracksController
def index
@running_tracks = RunningTracks.all
@running_track_ids_for_achievements_for_user = RunningTrack.running_track_ids_for_achievements_for_user(current_user.id)
end
end
And your view:
- unless @running_track_ids_with_achievements_for_user.include? track
= button_to "done", running_track_mark_as_done_path(track)
Upvotes: 1