setting up association for a has_one that is self-referential - ruby-on-rails

I have a User and a Reviewer table.
Reviewer tracks user_id and reviewer_id
now my association is setup like this:
# reviewer
has_one :reviewer
accepts_nested_attributes_for :reviewer
has_many :active_managements, class_name: 'Reviewer',
foreign_key: 'reviewer_id',
dependent: :destroy
has_one :my_reviewer, through: :reviewer, source: :user
what I'm trying to set up is that a User can have one reviewer, but can review many.
Now I'm trying to make it so that I can do
current_user.my_reviewer.first_name
f.ex and get the first name of the reviewer of the current user. However currently the
has_one :my_reviewer, through: :reviewer, source: :user
just goes to yourself. Which I think has to do with the source. If I try to change it to :reviewer I get this error:
Could not find the source association(s) :reviewer in model Reviewer. Try 'has_many :my_reviewer, :through => :reviewer, :source => <name>'. Is it one of user or reviewer_user?
and if I change it to reviewer_user
I get this error:
PG::UndefinedColumn: ERROR: column reviewers.reviewer_user_id does not exist
LINE 1: ... "users" INNER JOIN "reviewers" ON "users"."id" = "reviewers...
^
: SELECT "users".* FROM "users" INNER JOIN "reviewers" ON "users"."id" = "reviewers"."reviewer_user_id" WHERE "reviewers"."user_id" = $1 LIMIT 1

Reviewers are Users, right? So why not just get rid of the reviewer table and do this:
# user
has_many :reviewees, class_name: "User", foreign_key: "my_reviewer_id"
belongs_to :my_reviewer, class_name: "User"

Related

How to achieve has many through relation between users

I am building matching system between users, originally it was has and belongs to many association, but due to some validations and additional data i need for that join table, i need to have model for that one, and change it to has many through.
has_and_belongs_to_many(:likes,
class_name: :User,
join_table: :user_likes,
foreign_key: :user_liker_id,
association_foreign_key: :user_liked_id)
has_and_belongs_to_many(:likeds,
class_name: :User,
join_table: :user_likes,
foreign_key: :user_liked_id,
association_foreign_key: :user_liker_id)
This is old implementation which needs to be converted to has_many through. As you can see, with this one i can retrieve all users which i liked and users which liked me. I have created UserLike model but i don't have any success with making it workable inside user model. What i already tried is following:
user.rb
has_many :user_likes
has_many :likes, through: :user_likes, foreign_key: :user_liked_id
has_many :likeds, through: :user_likes, foreign_key: :user_liker_id
user_like.rb
class UserLike < ApplicationRecord
belongs_to :user
end
This implementation raises following exception:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) "like" or :likes in model UserLike. Try 'has_many :likes, :through => :user_likes, :source => '. Is it one of user or user_likes?
I don't know what i am missing here, any help would be appreciated :)
When creating a join table that joins the same table twice you need two separate associations since the user can be in either foreign key:
class User < ApplicationRecord
has_many :likes_as_liker,
class_name: 'Like',
foreign_key: 'liker_id'
has_many :likes_as_liked,
class_name: 'Like',
foreign_key: 'liked_id'
end
class Like < ApplicationRecord
belongs_to :liker, class_name: 'User'
belongs_to :liked, class_name: 'User'
end
With our two has_many associations done we can start creating indirect associations:
class User < ApplicationRecord
has_many :likes_as_liker,
class_name: 'Like',
foreign_key: 'liker_id'
has_many :likes_as_liked,
class_name: 'Like',
foreign_key: 'liked_id'
has_many :likers,
through: :likes_as_liked
has_many :liked_users,
through: :likes_as_liker,
source: :liked
end
Which works perfectly:
irb(main):002:0> u.likers
User Load (2.8ms) SELECT "users".* FROM "users" INNER JOIN "likes" ON "users"."id" = "likes"."liker_id" WHERE "likes"."liked_id" = $1 LIMIT $2 [["liked_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):003:0> u.liked_users
User Load (1.3ms) SELECT "users".* FROM "users" INNER JOIN "likes" ON "users"."id" = "likes"."liked_id" WHERE "likes"."liker_id" = $1 LIMIT $2 [["liker_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
As per the description mentioned in the post you are trying to find the users which have liked my post and the users whom I have liked.
Below mentioned code does not tell rails that likes and likeds are actually users.
has_many :user_likes
has_many :likes, through: :user_likes, foreign_key: :user_liked_id
has_many :likeds, through: :user_likes, foreign_key: :user_liker_id
Modify it to below
has_many :user_likes
has_many :likes, through: :user_likes, foreign_key: :user_liked_id, source: :liked
has_many :likeds, through: :user_likes, foreign_key: :user_liker_id, source: :liked
Best Approach is specifying class along with class name so that rails know in which class is mapped to this foreign key when specifying customized relation names
When your association name is different than the name used at the :through you have to define the source parameter. If you look at the exception message it explicitly asks you to do it.
Hope it answers the question.

Joining Nested Associations (Multiple Level)

I have the following models and relationships:
A User has many Offers (where he/she is the seller), an Offer has many Purchases, a Purchase has many Accbooks
Models and associations:
class User < ApplicationRecord
has_many :offers, foreign_key: :seller_id
has_many :purchases, foreign_key: :buyer_id
end
class Offer < ApplicationRecord
has_many :purchases
belongs_to :seller, class_name: 'User'
end
class Purchase < ApplicationRecord
belongs_to :offer
belongs_to :buyer, class_name: 'User'
has_one :seller, through: :offer
has_many :accbooks, class_name: 'Admin::Accbook', foreign_key: 'purchase_id'
end
module Admin
class Accbook < ApplicationRecord
belongs_to :purchase
end
end
I want to get all the Accbooks of any given user (as a seller). The equivalent SQL statement would look like this:
SELECT "accbooks".*
FROM "accbooks"
INNER JOIN "purchases" ON "purchases"."id" = "accbooks"."purchase_id"
INNER JOIN "offers" ON "offers"."id" = "purchases"."offer_id"
INNER JOIN "users" ON "users"."id" = "offers"."seller_id"
WHERE "users"."id" = ?
So far I've tried this:
Admin::Accbook.joins( {purchase: :offer} )
Which gives me this SQL as a result:
SELECT "accbooks".*
FROM "accbooks"
INNER JOIN "purchases" ON "purchases"."id" = "accbooks"."purchase_id"
INNER JOIN "offers" ON "offers"."id" = "purchases"."offer_id"
Now I don´t know how to add the join to the User model, and then how to add the Where condition.
Thanks for any insight.
You can joins the relations together and apply where clause on the joined relations:
Admin::Accbook
.joins(purchase: :offer)
.where(offers: { seller_id: 123 })
A thing to know, where uses the DB table's name. joins (and includes, eager_load, etc) uses the relation name. This is why we have:
Admin::Accbook
.joins(purchase: :offer)
# ^^^^^ relation name
.where(offers: { seller_id: 123 })
# ^^^^^^ table name
Try Adding following association in users.rb
has_many :accbooks, through: :purchases
So your problem is user is acting as 2 roles for same accounts. You can try something like below stuff
class User < ApplicationRecord
has_many :offers, foreign_key: :seller_id
has_many :purchases, foreign_key: :buyer_id
has_many :offers_purchases,
through: :offers,
:class_name => 'Purchase',
:foreign_key => 'offer_id',
:source => :purchases
end

Rails self join with has_many through relationship

Users can follow other Users through FollowingRelationship
I would like to be able to say
User.first.followings and it returns a list of Users
This is not working:
Class User
has_many :following_relationships
has_many :followings, through: :following_relationships, foreign_key: :following_id, source: :user
end
Class FollowingRelationship
attr_accessible :following_id, :follower_id
belongs_to :followings, class_name: "User"
end
User.first.followings gives this in console:
SELECT "users".* FROM "users" INNER JOIN "following_relationships" ON "users"."id" = "following_relationships"."user_id" WHERE "following_relationships"."user_id" = 1
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: following_relationships.user_id: SELECT "users".* FROM "users" INNER JOIN "following_relationships" ON "users"."id" = "following_relationships"."user_id" WHERE "following_relationships"."user_id" = 1
Anyone see what piece I am missing?
I think the problem is that FollowingRelationship model doesn't have any relation with User.
It should have belongs_to :user since you specify source: :user, shouldn't it?
I don't quite understand your models and relations between them but belongs_to :followings looks rather strange.
belongs_to uses singular form since it can't belong to more than one object.
UPDATE 1
To be more clear.
I think you should have
class FollowingRelationship
…
belongs_to :user
belongs_to :follower
end
That means followings_relationships table should have user_id and follower_id columns.
UPDATE 2
After some conversation we have figured it out.
Here is the code you should have
class User < ActiveRecord::Base
has_many :fade_relationships, foreign_key: :faded_id
has_many :fadings, through: :fade_relationships, source: :fading
end
class FadeRelationship < ActiveRecord::Base
attr_accessible :faded_id, :fading_id
belongs_to :fading, class_name: "User"
end

Join table on one-to-many cannot update

Ok, check this weirdness out. I have two types of users, leaders and followers (who each have their own subclasses for reasons that please for the love of god I don't want to go into and moreover are not germaine to this discussion).
class Admin < Account
has_many :leader_follower_relationships, foreign_key: :leader_id, dependent: :destroy
has_many :followers, through: :leader_follower_relationships
end
class Follower < Account
has_one :follower_leader_relationship, class_name: "LeaderfollwerRelationship",
dependent: :destroy
has_one :leader, through: :follower_leader_relationship
end
class LeaderFollowerRelationship < ActiveRecord::Base
belongs_to :follower, class_name: "Follower", foreign_key: :artist_id
belongs_to :leader, class_name: "Admin", foreign_key: :leader_id
end
Anyway, I can establish the relationship using a has_one, but I can't update it:
follower1.leader = leader1
(0.3ms) BEGIN
Account Exists (1.1ms) SELECT 1 AS one FROM "accounts" WHERE "accounts"."auth_token" IS NULL LIMIT 1
Account Exists (0.4ms) SELECT 1 AS one FROM "accounts" WHERE "accounts"."email_address" = 'leader1#example.com' LIMIT 1
SQL (0.6ms) ...
(0.5ms) COMMIT
follower1.leader = leader2
(0.3ms) BEGIN
(1.8ms) UPDATE "leader_follower_relationships" SET "leader_id" = 3 WHERE "leader_follower_relationships"."" IS NULL
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PGError: ERROR: zero-length delimited identifier at or near """"
LINE 1: ...ader_id" = 3 WHERE "leader_follower_relationships"."" IS NULL
If my follower can have many leaders (using has_many), I can both create and update:
class Follower < Account
has_many :follower_leader_relationships, class_name: "LeaderfollwerRelationship",
dependent: :destroy
has_many :leaders, through: :follower_leader_relationships
end
These commands work in succession:
follower1.leaders = [leader1]
follower1.leaders = [leader2]
The ActiveRecord error makes me think that the table for your LeaderFollowerRelationship model does not have an id column. If that's not it, can you post the related bits of schema.rb?

in Rails, need to set many-to-many relationship and one-to-many relationships between the same two models

I have an app in which projects belong to one owner (a user) and have and belong to many members (users).
I have the following code set up in the models to handle these associations. Note, project has owner_id in its database but not member_id
project model code
attr_accessible :description, :name, :owner_id, :avatar_url
belongs_to :owner, foreign_key: :owner_id, class_name: "User"
has_and_belongs_to_many :members, class_name: "User"
user model code
has_and_belongs_to_many :projects
has_many :owned_projects, class_name: "Project", foreign_key: "owner_id"
has_many :associated_projects, class_name: "Project", foreign_key: "member_id"
On a given project, I can call p.owner but not p.members. And I for a user I can call u.owned_projects or u.projects but not u.associated_projects.
When I try those commands, I get the following errors
1.9.3p194 :003 > p.members
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "projects_users" ON "users"."id" = "projects_users"."user_id" WHERE "projects_users"."project_id" = 3
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: projects_users: SELECT "users".* FROM "users" INNER JOIN "projects_users" ON "users"."id" = "projects_users"."user_id" WHERE "projects_users"."project_id" = 3
and
1.9.3p194 :007 > ryan.associated_projects
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."member_id" = 14593
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: projects.member_id: SELECT "projects".* FROM "projects" WHERE "projects"."member_id" = 14593
How can I set up these models so they'll work as I intend?
Note: I modeled my code after - Two has_many links between the same models but it hasn't helped.
You have to have a has_many_and_belongs_to relationship in your User and Project models. Also you need a intermediate table inbetween. http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
Class Project < ActiveRecord::Base
attr_accessible :description, :name, :owner_id, :avatar_url
belongs_to :owner, foreign_key: :owner_id, class_name: "User"
has_and_belongs_to_many :members, class_name: "User"
end
Class User < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :owned_projects, class_name: "Project", foreign_key: "owner_id"
end
As said, you will have to have a middle table between member Project and User(member).
You had that error when you tried to see project members: no such table: projects_users
Projects_Users Table
user_id | project_id

Resources