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
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
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.
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.
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
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.