Using includes on a custom association in rails - ruby-on-rails

I have a class Team that has many Matchups
class Team < ActiveRecord::Base
has_many(
:home_matchups,
class_name: "Matchup",
foreign_key: :team_1_id,
primary_key: :id
)
has_many(
:away_matchups,
class_name: "Matchup",
foreign_key: :team_2_id,
primary_key: :id
)
def matchups
Matchup.where("team_1_id = ? OR team_2_id = ?", id, id)
end
end
and
class Matchup < ActiveRecord::Base
validates :team_1_id, :team_2_id, :week, presence: true
belongs_to(
:team_1,
class_name: "Team",
foreign_key: :team_1_id,
primary_key: :id
)
belongs_to(
:team_2,
class_name: "Team",
foreign_key: :team_2_id,
primary_key: :id
)
has_one :league, through: :team_1, source: :league
end
This all works fine, until I want to use includes like this: Team.includes(:matchups) because although matchups returns an active record relation I can't use it. This is a problem later because I need this kind of information for many teams and I don't want to make a query for every team. Can anyone tell me how I could use includes to get the combination?

Like Sean Huber said, include the home and away matchups, then refer to them with .matchups
def matchups
(home_matchups + away_matchups).sort_by(&:week)
end
It's a little slower (maybe), but you'll be guaranteed to use the relation if it's loaded.
The general approach here - include the relations like you would, then use convenience methods that refer to those relations - whether it's in a view or another model method like this example.

Related

how can I do inverse of self-reference with rails

I have the user model like :
has_many :users, class_name: 'User'
belongs_to :master_user, class_name: 'User', optional: true, inverse_of: :users
I Would like to find :
User.first.master_user it's ok
MasterUser.users but get an error : "NameError: uninitialized constant MasterUser"
You are getting that error because you have not defined a MasterUser model. I am guessing you only have a User model as described in your question. If you want to find the users belonging to a "master_user" then you need to find a "master_user" first, then request its users. It would look something like this:
user_with_a_master = User.where.not(master_user_id: nil).first
master = user_with_a_master.master_user
master_users = master.users
Here is an example of how to properly setup a self-referential association with a bit less confusing naming:
class User < ApplicationRecord
belongs_to :manager
class_name: 'User', # requied here since it cannot be derided from the name
optional: true,
inverse_of: :subordinates
has_many :subordinates,
class_name: 'User', # requied here since it cannot be derided from the name
foreign_key: :manager_id, # what column on the users table should we join
inverse_of: :manager
end
"Managers" here are not a separate class. While you could use single table inheritance to that purpose you should probally get the basics figured out first. Even if you did have a MasterUser class you would get NoMethodError since you're calling .users on the class and not an instance of the class - that will never work and is a very common beginner misstake.
Note that this strictly speaking would actually work without the inverse_of: option which is really just used to explicity set the two way binding in memory.
So in your case it should look like:
class User < ApplicationRecord
# class_name isn't required since it can be derided from the name
has_many :users,
foreign_key: :master_user_id,
inverse_of: :master_user
belongs_to :master_user,
class_name: 'User', # requied here since it cannot be derided from the name
optional: true,
inverse_of: :users
end
Note that the users table must have a master_user_id column.

Rails - How to set up a relationship where has_many through could be multiple columns

I've been out of the rails dev world for a little while and am a bit rusty when it comes to setting up relationships and joins between tables, so apologies if this is a very simple question.
I have a Team model which has an id column.
I have a Match model which has home_team_id and away_team_id
columns.
I'd like to be able to call #team.matches and have any Match
where either the home_team_id or away_team_id equal the
#team.id show up.
I can achieve it with the following query, but it's far from ideal.
#matches = Match.where(home_team_id: #team.id) || Match.where(away_team_id: #team.id))
So what's the recommended way to set up my models and relationships to achieve the above behaviour?
Thanks in advance!
This is inspired by #Joel_Blum's answer. I think it's a bit cleaner and expands the utility through home_matches and away_matches scopes:
class Match < ApplicationRecord
belongs_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
end
class Team < ApplicationRecord
has_many :home_matches, foreign_key: :home_team_id, class_name: "Match"
has_many :away_matches, foreign_key: :away_team_id, class_name: "Match"
def matches
home_matches.or(away_matches)
end
end
I've done similar with the following in the model:
def matches
Match.where('home_team_id = ? OR away_team_id = ?', self.id, self.id)
end
which at least gives you a scope. Then in your controller:
#matches = #team.matches
I'd be interested in any other ideas.
This is a possible model setup
class Match < ApplicationRecord
belogns_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
end
class Team < ApplicationRecord
has_many :matches,-> {
unscope(:where).where(home_team_id: self.id).or(where(away_team_id: self.id))
}
end
And now #team.matches should just work. The only tricky part is getting the has_many to work with 2 foreign keys by doing unscope(:where)

rails migration for matches between 2 teams

I'm a beginner working with ruby on rails at the moment, trying to nail down relations between my models Matches, Teams.
I want my matches to refer to the Teams table twice, once for a homeTeam and another for awayTeam. I guess my biggest question is, am I declaring relations incorrectly in my models?
At the moment I'm not even able to call the name of a team through a match.
I want to be able to call Team.matches to list all matches a team has, whether listed as homeTeam or awayTeam, and eventually be able to call the home or away matches for a particular team.
I also want to be able to call Match.teams/Match.homeTeam/Match.awayTeam to list the team(s) of a particular match.
Here's what I have so far:
Match migration
class CreateMatches < ActiveRecord::Migration
def change
create_table :matches do |t|
t.references :homeTeam
t.references :awayTeam
end
end
end
Match model
class Match < ActiveRecord::Base
has_one :homeTeam, :class_name => 'Team'
has_one :awayTeam, :class_name => 'Team'
end
Team model
class Team < ActiveRecord::Base
has_and_belongs_to_many :matches
end
Thanks in advance!
You can do it like this:
class Match < ActiveRecord::Base
belongs_to :hometeam, :class_name => 'Team'
belongs_to :awayteam, :class_name => 'Team'
#if you want to select all teams of a match, you can create a method
def teams
Team.find(self.hometeam_id, self.awayteam_id)
end
end
class Team < ActiveRecord::Base
has_many :home, class_name: 'Match', foreign_key: 'hometeam_id'
has_many :away, class_name: 'Match', foreign_key: 'awayteam_id'
end

how to setup two user_id columns in one model in rails?

I have two models
class User
has_many :disputes
end
class Dispute
belongs_to :user
end
So Dispute has :user_id, but my problem is that a Dispute has 2 sides - the claimant and the indicted, both of which are users.
I tried to solve this problem by creating two columns: :claimant_id and :indicted_id, and passing arguments like #dispute.claimant_id = current_user.id, but after that I can't use a relationship tricks like #dispute.user.name or #user.disputes with my :claimant_id and :indicted_id.
Is there any way to set up two :user_id (like a claimant and an indicted) in one model and still maintain the relationships between Dispute and User models?
You can go the route of having :claimant_id and :indicted_id on your users table, but in your class def you need
class Dispute < ActiveRecord::Base
belongs_to :claimant, class_name: 'User', :foreign_key => :claimant_id
belongs_to :indicted, class_name: 'User', :foreign_key => :indicted_id
end
Reference: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
I think this is a duplicate of Ruby on Rails Double Association
For your case use class_name as the second parameter to your belongs_to function and a hash with foreign_key and the id column name (as pointed out by #devkaoru), like so:
class Dispute < ActiveRecord::Base
belongs_to :claimant, class_name: 'User', :foreign_key => :claimant_id
belongs_to :indicted, class_name: 'User', :foreign_key => :indicted_id
end
I think that should do it up right.
Disclaimer: This is an untested solution

Rails: How can this be done with a has_one association?

My model has two columns, one named foo_id and the other bar_id.
I'm wondering if it's possible to turn these two simple methods into has_one associations:
class SomeModel < ActiveRecord::Base
def foobar_foo
Foobar.find( self.foo_id )
end
def foobar_bar
Foobar.find( self.bar_id )
end
end
Perhaps I've been staring at the documentation for too long, but I can't seem to find a way to tell Rails to use self.foo_id as the foreign key for the other model.
I'm aware that in most cases this should instead be a has_many :through or maybe a belongs_to, but for the sake of argument I'm interested to learn if this is possible with a has_one
Does this help?
has_one :foo, class_name: "Foobar", foreign_key: :foo_id
has_one :bar, class_name: "Foobar", foreign_key: :bar_id
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one
Finally had a chance to revisit this problem and incidentally, stumbled across a solution within the first few minutes of trying..
has_one :bar, primary_key: :bar_id, foreign_key: :id, class_name: 'Foobar'
has_one :foo, primary_key: :foo_id, foreign_key: :id, class_name: 'Foobar'
Works as intended.

Resources