How do I structure these 3 models with associations?
Issue
I'm having a lot of trouble setting up the model associations for a personal project I'm working on. Essentially I'm building a referee assigning system consisting of 3 models:
Assignor: the user who assigns referees to games
Referee: the user assigned to the game
Game: has 1-4 referees assigned
What I have so far in my models is:
class Assignor < ApplicationRecord
has_many :games
has_many :referees
has_many :assigned_referees, through:referees
end
class Game < ApplicationRecord
belongs_to :assignor
has_many :referees
end
class Referee < ApplicationRecord
has_many :assignors
has_many :games, through: :assignor
has_many :assigned_games, :through:assignor
end
What I'd like to do with these associations within my app is:
List the referees an assignor has => Assignor.referees
List the assignors a referee has => Referee.assignors
Where I'm having trouble is...
List the referees assigned to a game
List the referees NOT assigned to a game
-For Example:
If a user(Assignor) was to pull up a game and assign a referee, I want to make a drop down that populates with a list of referees NOT assigned
I would just go for a 3 way join table:
class Game < ApplicationRecord
has_many :referee_assignments
has_many :referees,
through: :referee_assignments,
inverse_of: :assigned_games
has_many :assigners,
through: :referee_assignments
def build_assignment(referee:, assigner:)
referee_assignments.new(
referee: referee.
assigner: assigner
)
end
end
class RefereeAssignment
# User who assigned the referee
belongs_to :assigner,
class_name: 'User'
belongs_to :game
belongs_to :referee
end
class Referee < ApplicationRecord
has_many :referee_assignments
has_many :assigners,
through: :referee_assignments
has_many :assigned_games,
through: :referee_assignments,
source: :game,
inverse_of: :referees
def build_assignment(game:, assigner:)
referee_assignments.new(
game: game,
assigner: assigner
)
end
end
List the referees assigned to a game
game = Game.find(1)
game.referees.each do |r|
# ...
end
List the referees not assigned to a game
referees = Referee.where.not(
id: game.referees
)
Using a subquery is the simplest possible way but you could also use NOT EXIST or a join.
Related
I have a Follow model with user_id and track_id. The Track model has an artist_id field.
What I want to do is count which artists have the most followers, but since users follow "tracks" and not "artists", I need to figure out how to do a count through the tracks.
So, what I was thinking was to do some sort of group by on a nested association. i.e. Group the Follow records by "track -> artist_id", somehow.
Then I could count the number of users for each.
Is that even possible? Is there more info that would be useful here?
You need to use has_many :through to establish the Artist <-> Tracks <-> User relationship.
class Artist < ApplicationRecord
has_many :tracks
has_many :users, through: :tracks
end
class Follow < ApplicationRecord
belongs_to :user
belongs_to :track
end
class Track < ApplicationRecord
belongs_to :artist
has_and_belongs_to_many :users, join_table: :follows
end
class User < ApplicationRecord
has_and_belongs_to_many :tracks
has_many :artists, through: :tracks, join_table: :follows
end
Then Rails can take care of the joins between Artist and User.
Artist.includes(:users).group(:id).count("users.id")
I've got a task where Garage should fetch cars from User model or if User doesn't have cars Garage should fetch cars from itself.
Ideally, I want to have all rails API for has_many relation. Is there any chance to do that?
class Garage
has_many :cars, through: :user || has_many :cars # pseudo-code
end
I would just name the associations differently and use a method to load the cars:
has_many :cars
has_many :user_cars, through: :user, source: :cars
def all_cars
user_cars.presence || cars
end
The full code necessary to solve your problem is unclear, but broadly speaking here's how I would go about performing this structural migration.
Firstly, define two clear associations:
class Garage < ApplicationRecord
has_many :user_cars, through: :user, class_name: 'Car'
has_many :cars
end
Then add some method(s) that attempt to fetch both, as you described:
class Garage < ApplicationRecord
has_many :user_cars, through: :user, class_name: 'Car'
has_many :cars
def cars
user_cars.presence || super
end
end
Then, when the data has all been migrated to the new structure, you can delete the old association:
class Garage < ApplicationRecord
has_many :cars, through: :user
end
I'm trying to setup the associations with my models for a Rails app that would manage escape room games.
Basically, there are three models.
Player
Game Master
Game
Players would request to play the game. Then the game master would approve each request before the game could start.
I'm assuming I associate my models like so, by using a has_many :through association
Here's an example of how I think the associations should be setup.
class GameMaster < ApplicationRecord
has_many :games
has_many :players, through: :games
end
class Game < ApplicationRecord
belongs_to :game_master
belongs_to :player
end
class Player < ApplicationRecord
has_many :games
has_many :game_masters, through: :games
end
Please let me know if I'm on the right path
Because one Game can have multiple Players and one Player can have multiple Games, you will need a table association that combine game_id and player_id in one composite key.
In this way, a player can request for play in many games.
The same is needed for Game Master, If a game can have multiple Masters, that can approve request from players.
The PlayerGame will set the players of the game, and the MasterGame will set de masters of the game:
So you must have:
class Player < ApplicationRecord
has_many :player_games
has_many :games, through: :player_games
end
class Master < ApplicationRecord
has_many :master_games
has_many :games, through: :master_games
end
class Game < ApplicationRecord
has_many :player_games
has_many :players, through: :player_games
has_many :master_games
has_many :masters, through: :master_games
end
class PlayerGame < ApplicationRecord
belongs_to :player
belongs_to :game
end
class MasterGame < ApplicationRecord
belongs_to :master
belongs_to :game
end
It can be different if a user can be a master. Let me know. Suggestion, draw a entity relationship model to easy understand.
I am working on an app where users have many quizzes and quizzes can have many users. I have set the relationships:
class User < ApplicationRecord
has_many :studies
has_many :quizzes, through: :studies
end
class Quiz < ApplicationRecord
has_many :studies
has_many :users, through: :studies
end
class Study < ApplicationRecord
belongs_to :user
belongs_to :quiz
end
I have a field in the Study table to store the score that the user made on the quiz, but I am unable to access the field. I have tried #quiz.studies.score and #quiz.study.score but Rails give me an undefined method. How to I access the field in a join model of a has_many though relationship?
#quiz.studies return the collection of studies. So you have to use first, last, each to get the score of the specific studies.
Try this:
#quiz.studies.first.score
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.