1

I'm fairly new to rails and I'm trying to figure out the best way to do this.

I have a players table and a teams table. They both are HABTM with each other and use a join table.

Models

class Player < ActiveRecord::Base
  has_and_belongs_to_many :teams
end

class Team < ActiveRecord::Base
  has_and_belongs_to_many :players
end

Controller

def players
  @players = Player.all
end

View

<%@players.each do |player|%>
    <tr>
      <td><%= link_to "Add", "steam://friends/add/#{player.steamid}"%></td>
      <td><%= link_to player.name, player%></td>
      <td><%=player.email%></td>
      <td><%=player.teams.teamname%></td>
    </tr>
<%end%>

First, I know teamname should be team_name.

I've tried building a loop that loops through the teams but this page has over 1600 players and so it takes a few minutes to run it.

Am I missing a better way to do this?

1 Answer 1

3

The reason this is slow is because you're executing another query for every user. It's what is known as an N+1 problem, as that's the algorithmic complexity.

It's very easy to resolve this by retrieving the data from the database more efficiently. You can tell Rails to load all of the necessary records using what's called Eager Loading.

In this case, it's as easy as this:

@players = Player.includes(:teams).all

Rails will execute a query to retreive all players, then execute a second query to retrieve all teams, and you'll access them just the same — your view doesn't need to change at all!

Sign up to request clarification or add additional context in comments.

4 Comments

Bam! I knew there was a way to speed it up. But that means the best method is to change how I've written my view above and put it back in as a loop, correct?
I added this <%player.teams.each do |team| puts team.teamname end%> and it loads sooo much faster, which is awesome. But I don't think my syntax using puts is correct, as it's not writing anything.
<%=player.teams.map{|team| team.teamname}%> Something like this is what I need, yeah? This seems to be displaying better though I think I need to wrap it in raw.
If you want to display multiple teams, yes, you will need to iterate through them for display. You could do that like this quite readily as well: player.teams.map(&:teamname).to_sentence. You don't want to use puts in an ERb view — you want to use the <%= opening “tag” to denote that the response should be rendered.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.