How to create follower and followee in Rails? - ruby-on-rails

I'm quite stuck in in Ruby on Rails relations and I really appreciate you help.
Have model User
class User < ActiveRecord::Base
has_many :followers, :through => :follows, foreign_key: "followee_id"
has_many :followees, :through => :follows, foreign_key: "follower_id"
end
and model Follow
class Follow < ActiveRecord::Base
belongs_to :followee, class_name: "User"
belongs_to :follower, class_name: "User"
end
but if want to create new follower like:
user.followers << User.first
the result is SystemStackError
Thank you for every help!

You have to try something like this:
class User < ActiveRecord::Base
has_many :follower_follows, foreign_key: :followee_id, class_name: "Follow"
has_many :followers, through: :follower_follows, source: :follower
has_many :followee_follows, foreign_key: :follower_id, class_name: "Follow"
has_many :followees, through: :followee_follows, source: :followee
end
Here follower_follows and followee_follows are join tables and source: :follower matches with the belong_to :follower identification in the Follow model and source: :followee matches with the belong_to :followee identification in the Follow model
I think this would work in your case

Related

Self-Referential Has Many Through with Custom Foreign Keys in Rails

I have a User model and a relationship table called ParentsChildren.
I'm trying to create two relationships on the User model so that User#children returns all of a users children and User#parents returns all of a users parents.
I've managed to get this working before, but I'm doing something wrong right this time, and I'm not sure what it is exactly.
class ParentsChildren < ApplicationRecord
self.table_name = 'parents_children'
belongs_to :parent_user, class_name: 'User'
belongs_to :child_user, class_name: 'User'
end
class User
has_many :parent_relationships, class_name: 'ParentsChildren', foreign_key: :parent_user_id
has_many :child_relationships, class_name: 'ParentsChildren', foreign_key: :child_user_id
has_many :children, through: :parent_relationships, class_name: 'User', source: :child_user
has_many :parents, through: :child_relationships, class_name: 'User', source: :parent_user
end
# => uninitialized constant ParentsChildren::ChildUser
Figured it out. The key was to drop 'User' as the class name for has_many :parents and has_many :users. It's inferred through the given sources.
class User
has_many :parent_relationships, foreign_key: :child_user_id,
class_name: 'ParentsChildren'
has_many :children, through: :parent_relationships,
source: :parent_user
has_many :child_relationships, foreign_key: :parent_user_id,
class_name: 'ParentsChildren'
has_many :parents, through: :child_relationships,
source: :child_user
end

why does column trades.item_id not exist?

I have a relationship model in which two Users can enter into a Trade for the exchange of two Items.
class User < ActiveRecord::Base
has_many :owned_items, class_name: "Item"
has_many :trades_received, class_name: "Trade", through: :owned_items, source: :trades
has_many :trades
has_many :wanted_items, class_name: "Item", through: :trades, source: :item
end
class Item < ActiveRecord::Base
belongs_to :owner, class_name: "User", foreign_key: :user_id
has_many :trades, dependent: :destroy
has_many :trade_requesters, through: :trades
has_many :trade_recipients, through: :trades
end
class Trade < ActiveRecord::Base
belongs_to :trade_requester, class_name: "User"
belongs_to :trade_recipient, class_name: "User"
belongs_to :wanted_item, class_name: "Item", foreign_key: :wanted_item_id
belongs_to :collateral_item, class_name: "Item", foreign_key: :collateral_item_id
end
The migration on my Trades table looks like this:
create_table :trades do |t|
t.belongs_to :trade_requester
t.belongs_to :trade_recipient
t.belongs_to :wanted_item
t.belongs_to :collateral_item
end
The stack trace leads to a helper method I'm using to list all Trade requests. That line says #trades = current_user.trades_received.requested.count, and then on down to the model association on User where has_many :owned_items, class_name: "Item". From my understanding, it looks like the trades_received method, which is called through: :owned_items and source: :trades should be referencing the :wanted_item_id foreign key in the migration. But it is not. It works if I create a migration to add item_id, but a Trade needs two items, and so I've split it up into the two wanted_item and collateral_item associations. How do I set that User association up so that it references the Item that is being requested by another User? Should Items has_many :trades, the way I have it, or should Items belongs_to :trades?
Full error:
PG::UndefinedColumn: ERROR: column trades.item_id does not exist
LINE 1: ...LECT COUNT(*) FROM "trades" INNER JOIN "items" ON "trades"."...
^
: SELECT COUNT(*) FROM "trades" INNER JOIN "items" ON "trades"."item_id" = "items"."id" WHERE "items"."user_id" = $1 AND "trades"."approved" IS NULL
tldr: I need to track a bunch of complex has_many :through associations, I don't think my data model is correct, and need help understanding why. Thank you.
You're setting up two has_many :through relationship between User and Item, with Trade as the join table for both. You got some relationship confused. Here is a setup based on your migration:
class User < ActiveRecord::Base
has_many :received_trades, class_name: "Trade", foreign_key: "trade_recipient"
has_many :requested_trades, class_name: "Trade", foreign_key: "trade_requester"
has_many :collateral_items, through: :received_trades
has_many :wanted_items, through: :requested_trades
end
class Item < ActiveRecord::Base
has_many :collateral_items, class_name: "Trade", foreign_key: "collateral_item"
has_many :wanted_items, class_name: "Trade", foreign_key: "wanted_item"
has_many :trade_requesters, through: :wanted_items
has_many :trade_recipients, through: :collateral_items
end
class Trade < ActiveRecord::Base
belongs_to :trade_requester, class_name: "User"
belongs_to :trade_recipient, class_name: "User"
belongs_to :wanted_item, class_name: "Item"
belongs_to :collateral_item, class_name: "Item"
end
##migration
create_table :trades do |t|
t.belongs_to :trade_requester
t.belongs_to :trade_recipient
t.belongs_to :wanted_item
t.belongs_to :collateral_item
end
Some explanation:
Item has_many :collateral_item ## item_id in table collateral_items
Item has_many :collateral_item, class_name: "Trade", foreign_key: "collateral_item"
##collateral_item_id in trades table.
Ok, well. The problem is here:
has_many :trades, dependent: :destroy
And in your Trade model:
belongs_to :wanted_item, ...
belongs_to :collateral_item, ..
Rails cannot handle this automatically.
You need to do one of this steps (depending on what you need in your app):
If you need separate associations:
class User < ActiveRecord::Base
has_many :trades_received, class_name: "Trade", through: :owned_items, source: :wantable_trades
end
class Item < ActiveRecord::Base
has_many :wanted_trades, class_name: 'Trade', inverse_of: :wanted_item, dependent: :destroy
has_many :collateral_trades, class_name: 'Trade', inverse_of: :collateral_item, dependent: :destroy
end
If you need all trades as single association:
Well, you will have a pain in the ass :) In this case you should either select associations manually, or rethink your data model.

Association from promotion rule to product not working

I'm trying to access products through promotion but can't.
In the command line: Promotion.last.promotion_rules.first.products
Returns an error of an uninitialized constant.
Here are my associations:
class Product
has_many :product_promotion_rules, class_name: 'ProductPromotionRule'
has_many :promotion_rules, through: :product_promotion_rules
end
class ProductPromotionRule
belongs_to :product
belongs_to :promotion_rule
end
class PromotionRule
has_many :product_promotion_rules, class_name: 'ProductPromotionRule', join_table: 'products_promotion_rules', foreign_key: :promotion_rule_id
has_many :products, through: :product_promotion_rules
belongs_to :promotion
end
class Promotion
has_many :promotion_rules
end
Within the Product model try:
has_many :product_promotion_rules, class_name: 'ProductPromotionRule', foreign_key: :product_id
has_many :promotion_rules, through: :product_promotion_rules, source: :promotion_rule
And within the PromotionRule model:
has_many :products, through: :product_promotion_rules, source: :product
Update: saw this, which you may want to remove/fix:
join_table: products_promotion_rules
Either change to:
join_table: product_promotion_rules
Or try removing it.

Trying to 'alias' a polymorphic has_many relationship

I have a User model:
class User < ActiveRecord::Base
has_many :tracks, dependent: :destroy
has_many :tracked_locations, through: :tracks, source: :tracking, source_type: 'Location'
and a Track model (think of it as 'following'):
class Track < ActiveRecord::Base
belongs_to :user
belongs_to :tracking, polymorphic: true
end
The idea here is I will have many models to track / follow so I am using polymorphism. For example I have a Location model:
class Location < ActiveRecord::Base
has_many :tracks, :as => :tracking, :dependent => :destroy
has_many :users, through: :tracks
Now in the console Location.first.users works fine along with User.first.tracked_locations.
Now I will be adding another polymorphic relationship along the lines of Flagged. The user can 'flag' another model with a note etc. So if I add has_many :users, through: :flagged to the Location model for example I need to differentiate between tracking users and flagged users.
I tried:
has_many :tracking_users, through: :tracks, source: :tracking, source_type: 'User'
but I get:
NoMethodError: undefined method `evaluators_for' for #<Location:0x007ff29e5409c8>
Can I even do this or am I missing something simple here?
UPDATE
Based on the answer below I figured it out:
has_many :tracking_users, through: :tracks, class_name: "User", foreign_key: "user_id", source: :user
I'm not 100% on this, but you could try:
has_many :tracking_users, through: :tracks, class_name: "User", foreign_key: "user_id", source: :user
Or you could also just create a class method and do it by hand.
def self.tracking_users
user_ids = tracks.collect(&:user_id)
User.where(id: user_ids)
end
edit: Had a brainfart, changed the "source" up there to :user. That tells what table to actually do the lookup in with the other attribute you've provided. of course it wouldn't be in :tracks

Rails has_many :through with the where clause

I've built easy Twitter application in Rails.
Now I would like to choose three random users that are not followed by the current user.
Here is my model:
class User < ActiveRecord::Base
has_many :tweets, dependent: :destroy
has_many :followerships, class_name: 'Followership', foreign_key: 'followed_id'
has_many :followedships, class_name: 'Followership', foreign_key: 'follower_id'
has_many :followers, through: :followerships, source: :follower
has_many :followed, through: :followedships, source: :followed
end
class Followership < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
class Tweet < ActiveRecord::Base
belongs_to :user
end
I tried to use the following query:
User.where.not(followers: current_user).order("RANDOM()").limit(3)
But it obviously doesn't work as I get no such column: users.follower_id error.
Is it even possible to do without sql query?
Thank you!
Try this:
already_following = current_user.followed.map(&:id)
#users = User.where.not(id: already_following).order("RANDOM()").limit(3)
Basically what I did, was got the list of users already being followed. Then you check the User table for id's not matching users already being followed.

Resources