I have two tables, Teams and Games. I am trying to set up the associations for these tables but running into some issues. Here is my Game model with it's associations:
# Game Model
class Game < ActiveRecord::Base
belongs_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
belongs_to :winning_team, class_name: "Team"
end
I may be overthinking this but I'm not sure how to set up my Team model to have_many Games.
With a simple has_many :games in my Team model, my tests return the following error:
Team Associations should have many games
Failure/Error: it { should have_many(:games) }
Expected Team to have a has_many association called games (Game does not have a team_id foreign key.)
I see that it's looking for team_id for Game, and since there's no team_id it errors. But in my Game table I have three foreign keys referencing the same class. So would I need to create a has_many for each home_team, away_team and winning_team?
You'll need something like:
class Team < ActiveRecord::Base
has_many :home_games, class_name: 'Game', foreign_key: 'home_team_id'
has_many :away_games, class_name: 'Game', foreign_key: 'away_team_id'
# This seems like a separate thing to me...
has_many :winning_games, class_name: 'Game', foreign_key: 'winning_team_id'
# Do not include winning games, since it would already be included
def games
self.home_games.to_a + self.away_games.to_a
end
end
Related
So I have three models, User, Team, and Game. Currently constructed as such.
class Team < ApplicationRecord
has_and_belongs_to_many :users
has_many :home_games, class_name: 'Game', foreign_key: 'home_team_id'
has_many :away_games, class_name: 'Game', foreign_key: 'away_team_id'
has_many :wins, class_name: 'Game', foreign_key: 'winner_id'
belongs_to :owner, class_name: 'User'
end
class User < ApplicationRecord
has_and_belongs_to_many :teams
has_many :teams_owned, class_name: 'Team', foreign_key: 'owner_id'
has_many :games, through: :teams
end
class Game < ApplicationRecord
belongs_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
belongs_to :winner, class_name: "Team", optional: true
end
I want to add an association between users and games. So I can call User.games and Game.users.
I tried adding this:
#in user model
has_many :games, through: :teams
#in team model
has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'
As the api docs said to do. But, when I try to call this association, I get an error that "game.team_id does not exist". Since each game has a home_team_id and away_team_id, but no team_id.
Did I just implement this extremely poorly? Or am I missing something? Any help appreciated.
I would say this isn't a really good solution.
In ActiveRecord you can't actually define associations where the foreign key can potentially be in two different columns like this:
has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'
It definitely won't work as Rails will still join the assocation as JOIN games ON games.team_id = teams.id. Just adding a WHERE clause to the query won't fix that. Since ActiveRecord actually creates a variety of different queries there is no option to simply provide a different join.
A kludge to make this work would be to add an instance method:
class Game < ApplicationRecord
def users
User.joins(:teams)
.where(teams: { id: home_team.id })
.or(Team.where(id: away_team.id))
end
end
As its not an actual association you cant join through it or use an sort of eager loading to avoid n+1 queries.
If you actually want to create a single association that you can join through you would need to add a join table between games and teams.
class Team < ApplicationRecord
# ...
has_many :game_teams
has_many :games, through: :game_teams
end
# rails g model game_team team:belongs_to game:belongs_to score:integer
class GameTeam < ApplicationRecord
belongs_to :team
belongs_to :game
end
class Game < ApplicationRecord
has_many :game_teams
has_many :teams, through: :game_teams
has_many :users, through: :teams
end
This is a better idea since it gives you a logical place to record the score per team.
As an aside if the composition of teams can change and accurate record keeping is important you might actually need additional join tables as the lineup when a game is played may not actually match the current lineup.
This question already has answers here:
Rails association with multiple foreign keys
(7 answers)
Closed 7 years ago.
I have two models Game and Team:
class Game < ActiveRecord::Base
belongs_to :home_team, class_name: 'Team', required: true
belongs_to :visitor_team, class_name: 'Team', required: true
end
and
class Team < ActiveRecord::Base
has_many :games
end
The relationship has_many :games doesn't work (I would have to specify class_name but in this case I have two class names). I have to differentiate home and visitor.
Any idea on how to design this?
Thanks
has_many will look for a foreign key with the same name as the class you're working in.
In this case, Rails will assume there is a team_id in the associated Game class, linking back to the Team.This isn't the case, so you need to be more explicit.
In this case, you also don't have a single link from a Game back to a Team. There are two different links between Game and Team, so you need to represent both of these links as associations:
class Team < ActiveRecord::Base
has_many :home_games, class_name: 'Game', foreign_key: :home_team_id
has_many :visitor_games, class_name: 'Game', foreign_key: :visitor_team_id
end
If you want to get all games for a given team, regardless of whether they are home or visitng games, you can define a method:
class Team < ActiveRecord::Base
has_many :home_games, class_name: 'Game', foreign_key: :home_team_id
has_many :visitor_games, class_name: 'Game', foreign_key: :visitor_team_id
def games
Game.where('home_team_id = ? or visitor_team_id = ?', id, id)
end
end
You have to specify the foreign_key, assuming you have columns home_team_id and visitor_team_id in games table:
has_many :home_games, class_name: 'Game', foreign_key: :home_team_id
has_many :visitor_games, class_name: 'Game', foreign_key: :visitor_team_id
I'm trying to find the best way to model a game in relation to teams.
The end goal is to be able to call things like:
#game.winner
#game.loser
#team.games
The first two relations are working, but the games one is not. Using has_many (see below), I get ERROR: column games.team_id does not exist which I would normally work around by using whatever the equivalent to :foreign_key => winner_id, but how can I have it checkout both winner_id and loser_id?
Is the only option to create a method in the teams model like so:
def games
won = Game.where(:winner => id)
lost = Game.where(:loser => id)
won + lost
end
So far what I'm doing is:
class Game < ActiveRecord::Base
has_one :winner, class_name: "Team"
has_one :loser, class_name: "Team"
end
class Team
has_many :games
# or something that actually works
end
You didn't say much about the problem. But I think you are working too hard. If a team can play several games and a game includes more than one team, then you you need a many to many relation. This requires a third table, and best practice is a has_many :through relation. It will look something like:
class Game < ActiveRecord::Base
has_many :teams, through: assignment
has_one :winner, class_name: 'Team', through: :assignment, order: 'score DESC'
has_one :loser, class_name: 'Team', through: :assignment, order: 'score ASC'
end
class Team
has_many :games, through: :assignment
end
class Assignment < ActiveRecord::Base
belongs_to :game
belongs_to :team
end
Now you have the winner and loser attributes, but you don't need to roll your own method to count games for a team. Just say team.games.count and similarly game.teams is the teams assigned to the game.
I have two models, a User and a Event, and I would to set up two different associations between them.
I want:
- a user to have many hosted events
- a user to have many attended events
- an event to belong to one user (owner / creator)
- an event to belong to many users (attendees)
It's a has and belongs to many relationship for the attended event and just has many for the hosted events, I just don't know how to set it up properly / the rails way.
I know I'd need a users_attended_events table
I think that would be something like this on the User model
has_many :events, through: :hosted_events
has_many :events, through: :attended_events
But what would I do about the Event's model?
I have:
belongs_to: user
alias_attribute :owner, :user
alias_attribute :creator, :user
has_many :users, through:???
this should be the users_attended_events table, so.. what would I put here? How do I name this "Attendees"
It doesn't sound like you need a has_many through association for users hosting events. Something like this should work sufficiently for that (in user.rb) if you have a hosted_by_id column on your events table:
has_many :hosted_events, class_name: "Event", foreign_key: "hosted_by_id"
For attending events assuming a join class with columns attendee_id and event_id:
class AttendeeEvent < ActiveRecord::Base
belongs_to :attendee, class_name: "User"
belongs_to :event
end
You can add the following association to user.rb:
has_many :attendee_events, foreign_key: "attendee_id"
has_many :attended_events, through: :attendee_events, source: :event
The source: :event option indicates that the target objects for this association are found from the event association on the joining object.
The associations in event.rb are then:
belongs_to :hosted_by, class_name: "User"
has_many :attendee_events
has_many :attendees, through: :attendee_events
For example I have 2 players game, with future to have more than one game in the same time.
I need to create associations that will give me the opportunity to simply call user.games and it will return all games where that user is involved, as first_user_id or second_user_id.
table games
desk
first_user_id
second_user_id
table users
id
Model User
has_namy ??
What I think you are asking for:
class Game < ActiveRecord::Base
has_one :first_user, foreign_key: :first_user_id, class_name: 'User'
has_one :second_user, foreign_key: :second_user_id, class_name: 'User'
end
class User < ActiveRecord::Base
has_many :first_user_games, foreign_key: :first_user_id, class_name: 'Game'
has_many :second_user_games, foreign_key: :second_user_id, class_name: 'Game'
def games
first_user_games + second_user_games
end
end
I think it is probably a bad sign that you need this though, and it will be a pain. Instead you could just add a UserGames table and UserGame join model, put a column called "player_number" on UserGame, and do has_many :games, through: :user_games on User to get all games, regardless of player number, and then use queries, filtering, scoping, etc. as needed to find only the games where the player was a certain number. Here are a few questions related to scoping/conditions for has_many ..., through: ... on columns in the join model, in case you want to be able to access lists of games in User where the player is a specific player #:
Rails complex has_many and has_many through
Scope with join on :has_many :through association