Rails has_many through a HABTM relationship - ruby-on-rails

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.

Related

Rails referral model with self referential association

I'm trying to create a Referral program on a Rails app and I struggle with the relationships.
My Referral model is pretty simple : godfather_id, godson_id, state
Both godfather and godson ids references an User, which can have many godsons but only one godfather.
class Referral < ApplicationRecord
belongs_to :user
belongs_to :godson, class_name: 'User'
end
The issue comes in my User model. I wan't to be able to do user.godsons to get an array of godsons Users and user.godfather to get the godfather User.
I tried a few things and I think those two where the closest to what I need to do (User model simplified for the example).
class User < ApplicationRecord
has_many :referrals
has_many :godson, -> { where(godfather_id: id) }, through: :referrals
has_one :godfather, -> { where(godson_id: id) }, through: :referrals
end
class User < ApplicationRecord
has_many :godson_relations, class_name: 'Referral', foreign_key: 'godson_id'
has_many :godsons, through: :godson_relations
has_one :godfather_relation, class_name: 'Referral', foreign_key: 'godfather_id'
has_one :godfather, through: :godfather_relations
end
I'm really unsure about how to materialize this relationship, any help will be appreciated 🙏
To make an actual self-referential assocation you would just add a column on the users table that points back to the same table:
class AddGodfatherToUsers < ActiveRecord::Migration[6.1]
def change
add_reference :users, :godfather, null: true,
foreign_key: { to_table: :users }
end end
class User
belongs_to :god_father,
class_name: 'User',
optional: true,
inverse_of: :god_children
has_many :god_children,
class_name: 'User',
foreign_key: :god_father_id
inverse_of: :god_father
end
If you must store Referalls as a separate table you were kind of on the right track but you got the foreign keys backwards:
class Referral < ApplicationRecord
# you better be explicit here or its going to get extremely confusing
belongs_to :godfather, class_name: 'User'
belongs_to :godson, class_name: 'User'
end
class User < ApplicationRecord
has_many :referrals_as_godfather,
class_name: 'Referral', foreign_key: 'godfather_id'
has_one :referral_as_godson,
class_name: 'Referral',
foreign_key: 'godfather_id'
has_many :godsons, through: :referrals_as_godfather
has_one :godfather, through: :referral_as_godson
end
It should be noted that has_one in no way guarentees that a user can have only one referral (and thus one godfather). It just adds a LIMIT 1 to the query. You would have to enforce that with a uniqueness constraint and validations.

Ruby ORM polymorphic relationships, Active Record

So I am working with the Ruby ORM and trying to understand many-to-many syntax and polymorphism.
Here are my Active Record relationships so far.
class Association < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: "User"
end
and
class User < ActiveRecord::Base
has_many :associations
has_many :friends, through: :associations
end
I can't seem to get a list of friends per user when those friends are associated with more than one user. In other words some users have friends and these friends may have more than one user association too.
First off, these are not polymorphic associations. One uses polymorphic associations when a model could belong to many models, like a Comment model. A user can comment on a post, on a picture, on a project, so the Comment model could belong to any of these, so there we use Polymorphic associations. Read here to know more about it.
Well, the thing that you are asking is about Inverse Friends, and here is how you can implement it.
class User < ActiveRecord::Base
has_many :associations
has_many :friends, through: :associations
has_many :inverse_associations, class_name: "Association", foreign_key: :friend_id
has_many :inverse_friends, through: :inverse_associations, source: :user
end
class Assocation < ActiveRecord::Base
belongs_to :user
belogns_to :friend, class_name: 'User'
belongs_to :inverse_friend, class_name: 'User', foreign_key: :friend_id
end

Rails associations, how can I specify this relationship?

OS: Windows 7
Rails: 4.2.0
Hello, If I have two models lets say Game, and Team.
One game can have many teams, but teams can also belong to many games.
I can't seem to figure out the right way to do this, Belongs_to_many does not exist, and I don't think team can have_many games and game can have_many teams.
There are 2 ways to solve this:
One is like #Alex Pan's answer: using has_and_belongs_to_many
The other one is using belongs_to and has_many relation with a junction table.
in game.rb:
has_many :game_team_maps
has_many :teams, through: :game_team_maps
in team.rb
has_many :game_team_maps
has_many :games, through: :game_team_maps
in game_team_map.rb
belongs_to :game
belongs_to :team
In my opinion, the second way is more flexible and easier to maintain. I choose it for my own project.
There is a very detailed and useful cast from railscast that help you to solve this problem: http://railscasts.com/episodes/47-two-many-to-many
has_and_belongs_to_many
I believe is what you need
has_and_belongs_to_many
http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Or you create a joining model (e.g. TeamMembership), that belongs_to Team and Game
You want to use the has_and_belongs_to_many association.
Your models should look like this:
class Games < ActiveRecord::Base
has_and_belongs_to_many :teams #append this line to your model
end
class Teams < ActiveRecord::Base
has_and_belongs_to_many :games #append this line to your model
end
You will also want to create a join table for Games and Teams like so:
class CreateGamesTeams < ActiveRecord::Migration
def change
create_table :gamess_teams do |t|
t.integer :game_id
t.integer :team_id
end
end
end
Remember to run rake db:migrate to create this join table.
While has_and_belongs_to_many is certainly a valid option, most "games" that I know of have a home team and an away team, so it might make sense to do something like:
class Game < ActiveRecord::Base
belongs_to :home_team, class_name: 'Team'
belongs_to :away_team, class_name: 'Team'
end
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
end
You could also model it with a has_many :through:
class Game < ActiveRecord::Base
has_many :team_games
has_many :games, through: :team_games
end
class TeamGame < ActiveRecord::Base
belongs_to :game
belongs_to :team
scope :home, -> { where(home: true) }
scope :away, -> { where(home: false) }
end
class Team < ActiveRecord::Base
has_many :team_games
has_many :games, through: :team_games
has_many :home_games, -> { TeamGame.home }, through: :team_games, source: :game, class_name: 'TeamGame'
has_many :away_games, -> { TeamGame.away }, through: :team_games, source: :game, class_name: 'TeamGame'
end

A model that belongs_to two models or a polymorphic association?

I have three models
User
Product
Order
A user who submits products and other users can order that product.
Now I want to implement another model payment, where I as an admin pay the user.
Question
I am confused about what kind of association should i create between user, order and payment?
This is what I currently have :
in app/models/user.rb
class User < ActiveRecord::Base
has_many :products_selling, class_name: 'Product'
has_many :orders_received, class_name: 'Order', through: :products_selling, source: :orders
has_many :orders_made, class_name: 'Order'
has_many :products_ordering, class_name: 'Product', through: :orders_made, source: :product
has_one :payment, class_name: 'Payment', through: :orders_received
and in app/models/order.rb
class Order < ActiveRecord::Base
belongs_to :product
belongs_to :buyer, class_name: 'User', foreign_key: :user_id
has_one :seller, class_name: 'User', through: :product
has_one :payment, through: :seller
and in app/models/payment.rb
class Payment < ActiveRecord::Base
belongs_to :user
belongs_to :order
I am not sure what association should be used, I have been reading and there are examples using polymorphic: :true but they were all with has_many, where as in my case one order corresponds to one payment.
Associations
Polymorphic associations basically allow you to use a single table to manage multiple associations:
So if you wanted to link multiple tables to a single table, it will essentially create a pseudo object, which can be associated with different data types. We typically use polymorphic associations with tables which can be used by multiple models, examples including images, errors, posts etc:
In regards to relating polymorphic associations to has_many / has_one, I'm not entirely sure (I can research if you want)
--
Fix
In your case, I would do this:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :orders
has_many :purchases, class_name: 'Order', foreign_key: "buyer_id"
has_many :products
has_many :bought, class_name: 'Product', through: :purchases
end
#app/models/order.rb
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :product
has_one :payment
end
#app/models/payment.rb
Class Payment < ActiveRecord::Base
#fields - id | user_id | order_id | created_at | updated_at
belongs_to :order #-> user pays for order
belongs_to :user #-> user making the payment
end
This will allow you to create payments for each order - which is really what payments are for, right? (You pay for the order, not the product)

getting data from a join table

I have self referential model called Profile that is connected through the Relationship model.
class Profile < ActiveRecord::Base
belongs_to :user
has_many :accepted_relationships, class_name: 'Relationship', foreign_key: 'responder_id'
has_many :followers, through: :accepted_relationships, source: 'initiator'
has_many :initiated_relationships, class_name: 'Relationship', foreign_key: 'initiator_id'
has_many :followed_profiles, through: :initiated_relationships, source: 'responder'
has_many :groups
end
class Relationship < ActiveRecord::Base
belongs_to :responder, class_name: 'Profile', foreign_key: 'responder_id'
belongs_to :initiator, class_name: 'Profile', foreign_key: 'initiator_id'
belongs_to :group
end
class Group < ActiveRecord::Base
belongs_to :profile
has_many :relationships
attr_accessible :name
end
The problem is I don't know how to access data on the join model. If I do something like;
user.profiles[1].followers[1]
it will give me the profile I want. I would also like to have something like;
user.profiles[1].followers[1].assigned_group
so I could access the group that the relationship belongs to.
Is my design off, or am I overlooking something here?
I guess, Your Group will have many profiles and does your group require relationships, you can have either relationships or profiles in your group or profiles through relationships
I'm getting closer to an answer. I am passing a block to has_many
has_many accepted_relationships... do
def assigned_group
#code
end
end
I still haven't quite gotten it, but I think this is the route I need to take.

Resources