1

Bear with me as I'm a Rails noob. I have a basic league system. League -> Seasons -> Divisions -> Teams. I would like my Divisions and Teams to belong to any number of seasons. My models are as follows...

class Season < ActiveRecord::Base
  belongs_to :league
  has_many :season_division_teams
  has_many :divisions, :through => :season_division_teams
end

class Division < ActiveRecord::Base
  belongs_to :league
  has_many :seasons, :through => :season_division_teams
  has_many :season_division_teams
  has_many :teams, :through => :season_division_teams
end

class Team < ActiveRecord::Base
  belongs_to :league
  has_many :season_division_teams
  has_many :seasons, :through => :season_division_teams
  has_many :divisions, :through => :season_division_teams
end

class SeasonDivisionTeam < ActiveRecord::Base
  belongs_to :season
  belongs_to :division
  belongs_to :team
end

What I want to be able to do ultimately is say given that i'm in season '1', what divisions do I have, and then given those divisions what teams are part of those divisions (for the given season).

Season.Find(1).divisions.all do |division|
    # Do something with the teams that make of the division (for that season)
    # division.teams.count
end

I just can't get my head around how to do this in Rails, which seems very simple to me in SQL.

Thanks in advance for the help.

Edit:

Just to clarify, I'm trying to get the teams for a given season (with their divisions). With my model I know I can get the divisions for a given season. I just need to the the teams for those divisions and season.

Thanks again.

joero4
  • 210
  • 3
  • 8

1 Answers1

1

Use includes to eager-load the associations and avoid the N+1 performance hit when getting the teams:

@season = Season.includes(:divisions => :teams).find(1)
divisions = season.divisions
teams = divisions.map(&:teams).flatten.uniq

If you want to get all divisions and teams for that season only, then do this differently. Firstly, add the following line to your Season model:

has_many :teams, :through => :season_division_teams

Now you can simply do:

@season = Season.find(1)
divisions = @season.divisions
teams = @season.teams

Note that there's not much point in eager loading here since the database will only be hit twice (rather than once and then once again per division).

UPDATE

Lastly, if you want to get all divisions for the season, and then all teams within those divisions for that season, do as follows. You could use this code:

@season = Season.find(1)                     
divisions = @season.divisions
divisions.each do |division|
  puts "Division: #{division.name}"
  division.teams.where(:season_division_teams => {:season_id => @season.id}).each do |team|
    puts "Team: #{team.name}"
  end
end

But using eager-loading is the preferred method like so:

@season = Season.find(1)                     
divisions = @season.divisions.includes(:season_division_teams => :teams).
                    where(:divisions => {:season_division_teams => {:season_id => @season.id}})
divisions.each do |division| 
  puts "Division: #{division.name}"
  division.teams.each do |team|
    puts "Team: #{team.name}"
  end
end

This should eager load all teams for the associated divisions that meet the conditions that we merged in.

PinnyM
  • 35,165
  • 3
  • 73
  • 81
  • I tried your solution, but when I try to loop through teams and get the team.name, it simply prints out 'Team' and not 'Patriots' or 'Bruins', etc. Where am I going wrong. – joero4 Feb 18 '13 at 21:38
  • @joero4: Can you post the code you are using to print out the names? – PinnyM Feb 18 '13 at 21:46
  • <% season2 = Season.includes(:divisions => :teams).find(2) divisions = season2.divisions teams = divisions.map(&:teams) teams.each do |team| %> <%= team.name %> <% end %> – joero4 Feb 18 '13 at 21:50
  • @joero4: hmm, this looks right - have you tried restarting your server process? Also, have you verified that the attribute you're looking for is called `name` and that it contains the data you think it does? You can also try doing this in console and examine several of the team objects. – PinnyM Feb 18 '13 at 21:58
  • I restarted my server, and still nothing changes. I actually changed .name to .id, just in case and I get an error undefined method `id'. – joero4 Feb 18 '13 at 22:23
  • Ok, I tried going through the console, and in order to get the team name I had to do this..teams[0][1].name. The mapping is creating a 2 dimensional array (???). Also, I'm getting two results when I should only be getting 1. I have two records for one team in my db. But the team is linked to two different seasons. I should only get one team. Any idea why I would get two? – joero4 Feb 18 '13 at 22:32
  • Aha, I forgot to clarify that `map` will return an array of arrays. To fix this you can use `flatten`. And if you only want unique teams without duplicates, you can add `uniq` after that - updated answer. The only thing remaining is that this doesn't filter out teams of associated divisions that are only linked for _other_ seasons. Will this be a problem for you? If so, there's one more thing to add here... – PinnyM Feb 18 '13 at 22:59
  • We are almost there. Thank you so much. I need to only see teams associated with a given season. So if we have Season 1 - Div 1 - Team 1 and Season 2 - Div 1 - Team 2, I should only get Team 1 if I want teams for Season 1. and not see Team 2 even though Team 2 is associated with Div 1. Make sense? Thanks Again. – joero4 Feb 18 '13 at 23:26
  • I'm going to mark you answer as correct, as you have basically answered my question, but how could I figure out which teams below to what divisions? That will give me divisions and teams, but not broken down for teams per division which is what I really wanted. Thanks again. – joero4 Feb 19 '13 at 04:44
  • Thank you so much. A couple of issues (I'm so sorry if I'm driving you nuts). The first solution works, but for every team associated with a division the results are printed out. 3 team, 3 sets of results. The second solution didn't work. First there is a type with include(s). After fixing that, I get an error that "divsions"."season_division_teams" column does not exist. Thanks again. – joero4 Feb 19 '13 at 20:06
  • Oh dear...same error. 'SQLException: no such column: divisions.season_division_teams' – joero4 Feb 19 '13 at 22:03