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
Related
I have User model in my database. It has role. role can be either patient or doctor. I want to maintain doctor_patient table.
doctor_patient.rb
belongs_to :doctor, class_name: 'User'
belongs_to :patient, class_name: 'User'
a patient can belong to many doctors and a docor can have many patients. I am familier to regular or normal has_many through association but facing issues related to this scenarios where I have role in user model.
user.rb
user
has_many :doctor_patients
has_many :patients, :through => :doctor_patients, :class_name=> "User"
patient
has_many :doctor_patients
has_many :doctors, :through=> :doctor_patients, :class_name=> "User"
In ActiveRecord the assocation metadata (the reflection) is strored in a class attribute as a hash and the name is used as the key. So when you define multiple assocations with the same name you're just overwriting your previous assocation.
The solution is to use unique names for each assocation:
class User < ApplicationController
has_many :doctor_patients_as_doctor,
foreign_key: :doctor_id,
class_name: 'DoctorPatient'
has_many :patients,
through: :doctor_patients_as_doctor
has_many :doctor_patients_as_patient,
foreign_key: :patient_id,
class_name: 'DoctorPatient'
has_many :doctors,
through: :doctor_patients_as_patient
end
Also make sure you pluralize the table correctly and name it doctor_patients.
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.
I have one user model and one viewed_contractor model. I am treating user model as customer and contractor. customer can view many contractors by visiting their respective profile.Contractor can be viewed by many customers. I have customer_id and contractor_id in my viewed_contractor. I want to handle this relation as has_many through. Is it possible thorough has_many through?
It is possible. First, you'd need to specify the class_name option for the belongs_to associations in your ViewedContractor model so that they both refer to your User class. Then you could specify the has_many through: relations in your User model.
Something like this should work:
# viewed_contractor.rb
class ViewedContractor < ActiveRecord::Base
belongs_to :contractor, class_name: 'User', foreign_key: :contractor_id
belongs_to :customer, class_name: 'User', foreign_key: :customer_id
end
# user.rb
class User < ActiveRecord::Base
has_many :viewed_contractors_as_contractor, class_name: 'ViewedContractor', foreign_key: :contractor_id
has_many :viewed_contractors_as_customer, class_name: 'ViewedContractor', foreign_key: :customer_id
has_many :visited_contractors, through: :viewed_contractors_as_customer, source: :contractor
has_many :visited_customers, through: :viewed_contractors_as_contractor, source: :customer
end
I'm having problems with a Rails 4 join table. I have quite a simple setup which is working elsewhere in my application using a non-conventionally named table for users, groups and usergroupmemberships. I'm trying to set it up this time using the proper conventional naming and it's just not working.
Models involved are User, ManagementGroup and ManagementGroupsUser
db tables: management_groups_user, management_groups, users
app/models/user.rb
Class User < ActiveRecord::Base
...
has_many :management_groups, through: management_groups_users
has_many :management_groups_users
....
app/models/management_group.rb
class ManagementGroup < ActiveRecord::Base
has_many :users, through: :management_groups_users
has_many :management_groups_users
app/models/management_groups_user.rb
class ManagementGroupsUser < ActiveRecord::Base
belongs_to :users
belongs_to :management_groups
The association appears to work from with #user.management_groups_users but nothing else. I'm fairly sure this is a problem with naming / plurality but I can't figure it out.
This is the model which joins the remaining models user.rb and management_group
#app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
Since we are going to use model above to access another model management_group then
#app/models/user.rb
has_many :user_management_groups #This should come first
has_many :management_groups, through: user_management_groups
Since we are going to use model above to access another user model then
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
Now should work
Do it this way.
app/models/user.rb
has_many :user_management_groups
has_many :management_groups, through: user_management_groups
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
I hope these associations will help you.
This is another way if you pass foreign key and class name.
app/models/user.rb
has_many :user_management_groups, :foreign_key => "key", :class_name => "ClassName"
has_many :management_groups, through: user_management_groups, :foreign_key => "key", :class_name => "ClassName"
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user, class_name: "ClassName"
belongs_to :management_group, class_name: "ClassName"
This is another way around.
It's important to realize there is a convention rails uses for HABTM and has many through. HABTM does not have a model, so it needs to infer the table name which, as others point out, is both plural and alphabetical order.
If you are doing has many through and have a model, the convention is that it wants singular first word, plural second. See examples
User has and belongs to many groups.
HABTM: table name should be groups_users
Has Many Through: table name should be user_groups (flip order is more intuitive)
Model for the latter would be UserGroup. Has many through would specify it as through: :user_groups
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)