Adding counter cache to self join table in rails - ruby-on-rails

I am trying to add a counter cache on a a column in a self join association.
I have two models User and followings. User has followers and followees, who are from the user table itself.
User.rb
has_many :followings
has_many :followers, :through => :followings
has_many :followees, :through => :followings
Following.rb
class Following < ActiveRecord::Base
attr_accessible :followee_id, :follower_id
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
now i want to add counter cache on follower and followees. I have followers_count and followees_count columns in user table.
I tried
belongs_to :follower, :class_name => "User" , :counter_cache => true
But this doesn't return any data in the user table.
Any help would be appreciated.

It was long-long time ago, but
User.rb
class User < ActiveRecord::Base
has_many :followings_as_follower, class_name: 'Following', foreign_key: 'follower_id', dependent: :destroy
has_many :followings_as_followee, class_name: 'Following', foreign_key: 'followee_id', dependent: :destroy
has_many :followers, through: :followings_as_followee, source: :follower
has_many :followees, through: :followings_as_follower, source: :followee
def follow?(user)
followees.reload.include? user
end
def follow(user)
return if follow?(user)
followings_as_follower.create(followee: user)
end
def unfollow(user)
return unless follow?(user)
followings_as_follower.where(followee: user).first.destroy
end
end
Following.rb
class Following < ActiveRecord::Base
belongs_to :follower, class_name: 'User', counter_cache: :followees_count
belongs_to :followee, class_name: 'User', counter_cache: :followers_count
validates :follower, presence: true
validates :followee, presence: true
validates :followee, uniqueness: { scope: [:follower, :followee] }
end

Try this,
belongs_to :follower, foreign_key: 'the_id_of_foreign_key', class_name: 'User', counter_cache: :followers_count
You can use the column_name instead of true in counter_cache.

Related

Undefined method `create' for nil:NilClass

I am creating a project but whenever i trying to create grade for Class student using current_user.grade.create() or current_user.create_grade() getting an error "undefined method create for nil class"
My code is as follows.
Student.rb
class Student < User
has_one :user_grade , dependent: :destroy, foreign_key: 'user_id'
has_one :grade , through: :user_grade
end
Grade.rb
class Grade < ApplicationRecord
has_many :user_grades, dependent: :destroy
has_many :admins, through: :user_grades
has_many :teachers, through: :user_grades
has_many :students , through: :user_grades
has_many :guardians, through: :user_grades
has_many :posts, dependent: :destroy
validates_presence_of :cls
end
user_grade.rb
class UserGrade < ApplicationRecord
belongs_to :grade
belongs_to :admin, optional: true, class_name: 'Admin', foreign_key: 'user_id'
belongs_to :teacher, optional: true, class_name: 'Teacher', foreign_key: 'user_id'
belongs_to :student, optional: true, class_name: 'Student', foreign_key: 'user_id'
belongs_to :guardian, optional: true, class_name: 'Guardian', foreign_key: 'user_id'
end
Try using current_user.grade = Grade.new({}). Then calling current_user.save will save the association and save the new Grade.

Rails select from database table by multiple join throug other table

I have model Category (categories of Images)
class Category < ApplicationRecord
has_many :images, foreign_key: 'category_id', dependent: :destroy
has_many :subscriptions, foreign_key: "category_id", dependent: :destroy
has_many :subscribed_users, :through => :subscriptions, :source => :user
end
Model Category has many subscribtions and subscribed users
class Subscription < ApplicationRecord
belongs_to :user
belongs_to :category
validates :user_id, presence: true
validates :category_id, presence: true
end
class User < ApplicationRecord
has_many :images, foreign_key: 'user_id', dependent: :destroy
has_many :categories, foreign_key: 'user_id', dependent: :destroy
has_many :likes, foreign_key: "user_id", dependent: :destroy
has_many :liked_images, :through => :likes, :source => :images
has_many :comments, foreign_key: "user_id", dependent: :destroy
has_many :commented_images, :through => :comments, :source => :images
has_many :subscriptions, foreign_key: 'user_id', dependent: :destroy
has_many :subscribed_categories, :through => :subscriptions, :source => :categories
end
Also I have model Image
class Image < ApplicationRecord
belongs_to :user
belongs_to :category
has_many :likes, foreign_key: "image_id", dependent: :destroy
has_many :liking_users, :through => :likes, :source => :user
has_many :comments, foreign_key: "image_id", dependent: :destroy
has_many :commenting_users, :through => :comments, :source => :user
end
which has many likes and comments (comments and likes to Image, not to categories of image)
class Like < ApplicationRecord
belongs_to :user
belongs_to :image
validates :user_id, presence: true
validates :image_id, presence: true
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :image
validates :user_id, presence: true
validates :image_id, presence: true
end
I need to select from database 5 most popular categories.
The most popular category is category which has highest count of likes, comments and images at the same time.
I made one query, which select all images, that has the highest count of likes and comments:
#top_images = Image.left_outer_joins(:likes).distinct.select('images.*, COUNT(likes.*) AS likes_count').left_outer_joins(:comments).distinct.select('images.*, COUNT(comments.*) AS comments_count').group(
'images.id').order("likes_count DESC").order("comments_count DESC")
Also I make query which select categories, which has more images:
#top_categories_img_count = Category.left_outer_joins(:images).distinct.select('categories.*, COUNT(images.*) AS images_count').group(
'categories.id').order("images_count DESC")
But I have no idea how to select all categories which have highest count of images, likes and comments

Rails polymorphic join has_many, through returns impossible SQL

I have 3 models set up as follows:
class User < ActiveRecord::Base
has_many :interests, as: :interesting, dependent: :destroy
has_many :games, through: :interests, source: :interesting, source_type: 'Game'
has_many :people, through: :interests, source: :interesting, source_type: 'Person'
end
class Interest < ActiveRecord::Base
belongs_to :interesting, polymorphic: true
validates :user_id, presence: true
validates :interesting_id, presence: true
end
class Game < ActiveRecord::Base
has_many :users, through: :interests
has_many :interests, as: :interesting
end
class Person < ActiveRecord::Base
has_many :users, through: :interests
has_many :interests, as: :interesting
end
When I try to call user.games the SQL run on the database is
SELECT "games".* FROM "games"
INNER JOIN "interests"
ON "game"."id" = "interests"."interesting_id"
WHERE "interests"."interesting_id" = $1 AND
"interests"."interesting_type" = $2 AND
"interests"."interesting_type" = $3
[["interesting_id", 3],
["interesting_type", "User"],
["interesting_type", "Game"]]
so obviously nothing is returned. The query should work, as long as ["interesting_type", "User"] isn't included.
What am I doing wrong? What is the best way to set up the User class as well as the Game and Person class?
I'm using Rails v4.2.6
So to summarize, the following appears to work in this use case:
User < ActiveRecord::Base
has_many :interests, dependent: :destroy
has_many :games, through: :interests,
source: :interesting, source_type: 'Game'
has_many :people, through: :interests,
source: :interesting, source_type: 'Person'
end
class Interest < ActiveRecord::Base
belongs_to :user
belongs_to :interesting, polymorphic: true
validates :user_id, presence: true
validates :interesting_id, presence: true
end
Please trying this
class User < ActiveRecord::Base
has_many :interests, dependent: :destroy
has_many :games, as: :interesting, through: :interests, source_type: 'Game'
has_many :people, as: :interesting, through: :interests, source_type: 'Person'
end
class Interest < ActiveRecord::Base
belongs_to :interesting, polymorphic: true
validates :user_id, presence: true # I don't know the reason to use that if you use as polymorphic
validates :interesting_id, presence: true
end
The table_name interests it should have only the attributes :id:, :interesting_id, :interesting_type

Scoping with two foreign keys

I have the following schema:
I want to have the option to call proposals for both foreign_keys (author_id and editor_id) as well for separate ones (for example author_proposals and editor_proposals) and I need to have the option to lazy or eager load them (for example User.includes(:proposals) or without it with joins).
Update:
#I have the scopes which is like this:
class User < ActiveRecord::Base
has_many :author_proposals, class_name: 'Proposal', foreign_key: :author_id
has_many :editor_proposals, class_name: 'Proposal', foreign_key: :editor_id
end
class Proposal < ActiveRecord::Base
belongs_to :author, class_name: 'User', foreign_key: :author_id
belongs_to :editor, class_name: 'User', foreign_key: :editor_id
end
But I need a universal one which it will give me all the proposals (both author_proposals and editor_proposals) which it will also eager load them. Should I use conditions on has_many?
I would do something like this:
class User < ActiveRecord::Base
has_many :authored_proposals, class_name: 'Proposal', foreign_key: :author_id
has_many :editored_proposals, class_name: 'Proposal', foreign_key: :editor_id
def proposals
Proposal.where('author_id = :id OR editor_id = :id', { id: id }).distinct
end
end
class Proposal < ActiveRecord::Base
belongs_to :author, class_name: 'User', foreign_key: :author_id
belongs_to :editor, class_name: 'User', foreign_key: :editor_id
def users
User.where(id: [author_id, editor_id].uniq)
end
end
You can do something like:
class User < ActiveRecord::Base
has_many :authored_proposals, class_name: 'Proposal', foreign_key: :author_id
has_many :editored_proposals, class_name: 'Proposal', foreign_key: :editor_id
def proposals
authored_proposals | editored_proposals
end
end
class Proposal < ActiveRecord::Base
belongs_to :author, class_name: 'User', foreign_key: :author_id
belongs_to :editor, class_name: 'User', foreign_key: :editor_id
def users
author | editor
end
end
You can eager load proposals by doing: User.includes(:authored_proposals, :editored_proposals). This is not pure rails way, but seems cleaner to me.
You can also do :
class User < ActiveRecord::Base
has_many :authored_proposals, class_name: 'Proposal', foreign_key: :author_id
has_many :editored_proposals, class_name: 'Proposal', foreign_key: :editor_id
has_many : proposals, finder_sql: proc { "SELECT * FROM proposals WHERE (proposals.author_id = #{id} or proposals. editor_id = #{id})" }
end
Set your associations like this:
class User < ActiveRecord::Base
has_many :author_proposals, :class_name => "Proposal", :foreign_key => "author_id"
has_many :editor_proposals, :class_name => "Proposal", :foreign_key => "editor_id"
end
class Proposal < ActiveRecord::Base
belongs_to :author, :class_name => 'User', :foreign_key => "author_id"
belongs_to :editor, :class_name => 'User', :foreign_key => "editor_id"
end

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