getting data from a join table - ruby-on-rails

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.

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.

Rails has_many through a HABTM relationship

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.

mongoid model has two relations with another model - rails mongodb

I have a User model, and an Order model. There are two types of users: a seller, and a driver. Both sellers and drivers have many orders, and every order belongs to one seller and one driver. Thus, I want to be able to query for user.orders and have it return a particular user's orders, but also to successfully query for order.seller and order.driver. How can I establish these relationships?
This is what I came up with.. Pretty sure it works as intended.
Class User
has_many :sales, class_name: 'Order', inverse_of: :seller
has_many :pickups, class_name: 'Order', inverse_of: :driver
end
Class Order
belongs_to :seller, class_name: "User", inverse_of: :sale
belongs_to :driver, class_name: "User", inverse_of: :pickup
end
Seems like there is no clear way to have 2 relations share a field on another model. You could always do something like this if you have a field that differentiates drivers and sellers if they are a mix tho it won't work.
User.rb
has_and_belongs_to_many :orders
Order.rd
has_and_belongs_to_many :users
def seller
users.where(job: User.DRIVER)
end
UPDATE
User.rb
has_many :sales, class_name: 'Order', inverse_of: :seller
has_many :pickups, class_name: 'Order', inverse_of: :driver
def orders
Order.or(seller_id: self.id, driver_id: self.id)
end
Order.rb
belongs_to :seller, class_name: 'Order', inverse_of: :sales
belongs_to :driver, class_name: 'Order', inverse_of: :pickups

Rails one model two associations

I have a User model and a Task model. All users are the same and each user can create a new task and assign that task to another user. In the Task model I have an assigned_by column and an assigned_to column, so that anyone can create a new task and assign it to anyone else. Later I want to be able for each User to view all tasks assigned to them and all tasks they have assigned to someone else. To do this, I want to setup an association. Is it okay to do something like this?
class Task < ApplicationRecord
belongs_to :user, :foreign_key => 'assigned_by'
belongs_to :user, :foreign_key => 'assigned_to'
end
Where I have two foreign keys in the same model. Then in the User model I have:
class User < ApplicationRecord
has_many :tasks
end
Is this the proper way to do something like this?
What you probably want is to setup three tables:
class User < ApplicationRecord
has_many :assignments, foreign_key: 'assignee_id'
has_many :assignments_as_assigner, foreign_key: 'assignee_id'
has_many :tasks, through: :assignments
has_many :assigned_tasks, through: :assignments_as_assigner
has_many :created_tasks, class_name: 'Task'
foreign_key: 'creator'
end
class Task < ApplicationRecord
belongs_to :creator, class_name: 'User'
has_many :assignments
end
class Assignment < ApplicationRecord
belongs_to :assignee, class_name: 'User'
belongs_to :assigner, class_name: 'User'
belongs_to :task
end
This creates a one to many association so that a task can be assigned to many users.
Each association in the model has to have a unique name - otherwise you will overwrite the previous association.
The approach you suggested will not work as you can't define 2 methods with the same name (in this case both will be called user).
A better way would be calling the relation by what it actually.
For example
class Task < ApplicationRecord
belongs_to :assigned_by, class_name: 'User'
belongs_to :assigned_to, class_name: 'User'
end
You may also need to add a foreign_key option or call the foreign key in the DB assigned_by_id and assigned_to_id
Also, you will need to change your User model as tasks method is ambiguous.
class User < ApplicationRecord
has_many :tasks_delegated, foreign_key: 'assigned_by_id', class_name: 'Task'
has_many :tasks_assigned, foreign_key: 'assigned_to_id', class_name: 'Task'
end
Try to do like this to prevent overwriting.
class User < ApplicationRecord
has_many :owned_tasks, class_name: "Task", foreign_key: "owner_id"
has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end
class Task < ApplicationRecord
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

Rails has many through with same model

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

Resources