Here are my User and Relationships models
class User < ActiveRecord::Base
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :followers, through: passive_relationships, source: :follower
has_many :following, through: :active_relationships, source: :followed
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User", counter_cache: :followeds_count
belongs_to :followed, class_name: "User", counter_cache: :followers_count
validates :follower_id, presence: true
validates :followed_id, presence: true
validates :followed, uniqueness: { scope: [:follower, :followed] }
end
In Users Controller I can do:
#users = current_user.following
However I would like to turn this into a scope in my User model.
There are 2 things you may approach:
Find all users who are following someone
class User < ActiveRecord::Base
scope :following_to, -> (user_id) {
where(
"id IN ( SELECT followed_id
FROM relationships
WHERE follower_id = ?
)",
user_id
)
}
end
Find all users who are following anyone, that means they are a follower
class User < ActiveRecord::Base
scope :follower, -> {
where("id IN ( SELECT followed_id FROM relationships)")
}
end
Finally, you can use these scope as your expectation:
# Find all users who are following to User (id = 1)
User.following_to(1)
# Find all users who are following someone,
# aka they are a follower
User.follower
By using the Instance Method you can make a method For User Model
like this :
class User < ActiveRecord::Base
def following?
self.following.present?
end
end
By Using Scope you can call only the activerecord based query into the scope of model.
You should get also this way
scope :following?, lambda { |user|
{ user.following.present? }
And this should be call like in your controller
User.following?(current_user)
Related
I have Beta::Groups and Beta::Users.
Users can belong to more than one Group, but each Beta::User is also a regular User.
class Beta::User < ApplicationRecord
belongs_to :beta_group, class_name: "Beta::Group"
belongs_to :user
validates :user_id, uniqueness: { scope: :beta_group_id }
end
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
Effectively, Beta::User is just a join table between Beta:Group and User
When I load a Beta::Group, how can I retrieve the data for all the user's in that group?
#beta_group = Beta::Group.find(beta_user_params[:beta_group_id])
#beta_users = #beta_group.beta_users.all
The last line only retrieves the beta_users data obviously (just the beta_group_id and user_id). I don't want to have to then iterate through all the user_id's to get the actual User's full data.
So how I can set this up so that I can do something like #beta_group.users.all to retrieve all the data for each user that is a Beta:User?
EDIT - What I have tried
I tried adding this to the Beta::Group model:
has_many :users, through: :beta_users, source: :user
But when I call beta_group.users the query that runs is as follows, returning an empty array:
SELECT "beta_users".* FROM "beta_users" INNER JOIN "beta_users" "beta_users_users" ON "beta_users"."id" = "beta_users_users"."user_id" WHERE "beta_users_users"."beta_group_id" = $1 [["beta_group_id", 1]]
Notice that it is not joining correctly. It should be trying to join on "beta_users"."user_id" to "users.id"
I believe you can add a has_many through association:
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
# here:
has_many :users, through: :beta_users, source: :user
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
Then you should be able to call the association:
#beta_group = Beta::Group.find(beta_user_params[:beta_group_id])
#beta_users = #beta_group.users
EDIT - I believe you have an issue with scopes and the class_name because you have a User inside the Beta module.
Try adding the class_name: "::User" to both associations:
class Beta::User < ApplicationRecord
belongs_to :beta_group, class_name: "Beta::Group"
belongs_to :user, class_name: "::User" # without class_name it'll try a self association
end
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: "Beta::User", foreign_key: 'beta_group_id'
has_many :users, class_name: "::User", through: :beta_users # without a class_name it'll try to join the Beta::User
end
The problem was due to the fact that User is a table in both the top-level namespace and inside the Beta namespace.
Therefore, it is necessary to inform rails which table to use to create the SQL for, by specifying the top-level class in the relation, like this:
class_name: '::User'
Final code:
class Beta::Group < ApplicationRecord
has_many :beta_users, dependent: :destroy, class_name: 'Beta::User', foreign_key: 'beta_group_id'
has_many :users, through: :beta_users, source: :user, class_name: '::User'
validates :name, presence: true, uniqueness: true
def beta_users_count
beta_users.count
end
end
which generates the correct SQL:
SELECT "users".* FROM "users" INNER JOIN "beta_users" ON "users"."id" = "beta_users"."user_id" WHERE "users"."viewer" = $1 AND "beta_users"."beta_group_id" = $2 [["viewer", false], ["beta_group_id", 1]]
How do I find all admins using Rails 6 active record associations?
I have the following classes:
class Group < ApplicationRecord
has_many :relationships, dependent: :destroy
has_many :users, through: :relationships
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :relationships, dependent: :destroy
has_many :groups, through: :relationships
end
class Relationship < ApplicationRecord
belongs_to :user
belongs_to :group
validates :user_id, presence: true
validates :group_id, presence: true
validates :admin, inclusion: [true, false]
I'd like to add admins as a has_many relationship in Group.
Here's my first attempt at doing so:
has_many :admins, class_name: "User", through: :relationships
How would I filter those users whose relationship to the group has the admin attribute set to true?
Associations can be scoped using the normal query syntax.
class Group < ApplicationRecord
...
has_many :admins,
-> { where({ relationships: { admin: true} }) },
through: :relationships,
source: :user
end
source is necessary because otherwise it will try to do the has_many through Relationships.admins.
group.admins is equivalent to group.users.where({ relationships: { admin: true} }).
You could've modified your Group model to something like:
class Group < ApplicationRecord
scope :admins, -> { where(relationships: { admin: true }) }
has_many :relationships, dependent: :destroy
has_many :users, through: :relationships
end
From it you can use Group.joins(:users).admins to get groups with assoiciated admins.
I am building a ROR app with Users and Groups and I want to be able to link them with an associative model called Memberships.
My problem is that when I try to create a group with one member, the members array for the new group is always empty.
I tried creating a group like this:
def create (user)
#group = Group.new(create_group_params)
user.join(#group)
user.save
#group.save
end
but #group.members is empty when I print it out. How can I make the user a member of the group?
Here are my models:
Group.rb
class Group < ActiveRecord::Base
has_many :passive_memberships, class_name: "Membership",
foreign_key: "club_id",
dependent: :destroy
has_many :members, through: :passive_memberships, source: :member
end
User.rb
class User < ActiveRecord::Base
has_many :active_memberships, class_name: "Membership",
foreign_key: "member_id",
dependent: :destroy
has_many :memberships, through: :active_memberships, source: :club
def join(group)
active_memberships.create(club_id: group.id)
end
end
Membership.rb
class Membership < ActiveRecord::Base
belongs_to :member, class_name: "User"
belongs_to :club, class_name: "Group"
validates :member_id, presence: true
validates :club_id, presence: true
end
Oh. I have to save the group before the user can join it.
def create (user)
#group = Group.new(create_group_params)
#group.save
user.join(#group)
user.save
end
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.
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.