I have association in rails 3.1
has_many :likes, :dependent => :destroy
I wanted to add a join with a condition in the above association
likes = user.likes.joins('INNER JOIN posts ON posts.id = likes.likable_id and likes.likable_type = "Post"').where("posts.is_published", true)
So instead of using user.likes.joins(). I only wanted to use user.likes. Scope is also one option but is it possible to add that in association.
Try something like this:
class User < ActiveRecord::Base
has_many :likes, conditions: {"likes.likable_type = 'Post'", "posts.is_published IS true"}, dependent: :destroy
end
Related
In my case, users are related to other users through friendships. I want to order my users by the number of friends/friendships they have.
class User < ActiveRecord::Base
has_many :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
end
NB In my case I am using postgres
I am currently using
a = User.all.collect {|x| [x.id, x.friends.length]};
a = a.sort {|x,y| y[-1] <=> x[-1]}
Where x.friends is a method which returns all friends via Friendship.where('friend_id = ? OR user_id = ?', self, self). I believe this is suboptimal as it makes a request per user.
I am not sure that my request will work properly, because I can't test on on a real DB now. But you should do it like this:
User.select(users.*, SUM(friendships.id)).joins("INNER JOIN users as friendships ON users.id = friendships.friend_id").group(users.id).order("SUM(friendships.id)")
User.select(users.*, SUM(inverse_friendships.id)).joins("INNER JOIN users as inverse_friendships ON users.friend_id = inverse_friendships.id").group(users.id).order("SUM(inverse_friendships.id)")
It will work only with PostgreSQL 9.1 and higher.
My User model looks like this:
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
What I want is the raw SQL query to get users that a given user follows and is followed back by.
Currently I've only gotten as far as getting the ids of users I'm following:
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = #{user.id}"
I have a rails methods that returns mutual friends, but I want the SQL equivalent:
def mutual_friends
# interesect both arrays to find similar elements
self.followed_users & self.followers
end
Something like this should work
SELECT followed_id
FROM relationships
WHERE follower_id = #{user.id} AND followed_id IN (
SELECT follower_id FROM relationships WHERE followed_id = #{user.id}
)
Please use it like this, let me know if it works for you.
has_many :company_friendships, autosave: true
has_many :company_friends, through: :company_friendships, autosave: true
has_many :inverse_company_friendships, class_name: "CompanyFriendship", foreign_key: "company_friend_id", autosave: true
has_many :inverse_company_friends, through: :inverse_company_friendships, source: :company, autosave: true
def mutual_company_friends
Company.where(id: (company_friends | inverse_company_friends).map(&:id))
end
So if I have the following relationship
class Item < ActiveRecord::Base
has_many :item_user_relationships
has_many :users, :through => :item_user_relationships
end
class User < ActiveRecord::Base
has_many :item_user_relationships
has_many :items, :through => :item_user_relationships
end
class ItemUserRelationship < ActiveRecord::Base
belongs_to :item
belongs_to :user
attr_accessible :role
end
What's the rails way to include the role attribute when listing all the Users of an Item?
#users = #item.users # I want to include the role as part of a user
Thanks!
UPDATE: I'm still having trouble with this. My goal is to get an array of User models that have their role included in the attributes.
I'm note sure if I understand you correctly, but maybe this is what you want?
#users = #item.users
#users.each do |user|
puts user.item_user_relationships.first.role
end
I have the following models:
class Image < ActiveRecord::Base
belongs_to :gallery
has_many :bookmarks
has_many :gallery_tags, :foreign_key => :gallery_id
end
class Bookmark < ActiveRecord::Base
belongs_to :user
belongs_to :image
has_many :gallery_tags, :through => :image, :source => :gallery_tags
end
class GalleryTag < ActiveRecord::Base
belongs_to :gallery
belongs_to :tag
end
class Gallery < ActiveRecord::Base
belongs_to :provider
has_many :images
belongs_to :user
has_many :gallery_tags
has_many :tags, :through => :gallery_tags
end
class Tag < ActiveRecord::Base
end
class User < ActiveRecord::Base
has_many :bookmarks
has_many :galleries
end
I'd like to be able to do
User.find(1).bookmarked_tags
and retrieve all tags associated with all of the user's bookmarked images via the galleries associated with the images. Tags are associated with galleries.
The query would end up looking like this:
SELECT
tags.*
FROM
tags
INNER JOIN gallery_tags ON gallery_tags.tag_id = tags.id
INNER JOIN images ON gallery_tags.gallery_id = images.gallery_id
INNER JOIN bookmarks ON images.id = bookmarks.image_id
WHERE
bookmarks.user_id = 1
GROUP BY
tags.id;
I've tried adding
has_many :tags, :through => :gallery_tags, :foreign_key => :gallery_id
to Image, which causes Image.find(34).tags to result in
SELECT `tags`.* FROM `tags` INNER JOIN `gallery_tags` ON `tags`.id = `gallery_tags`.tag_id WHERE ((`gallery_tags`.gallery_id = 34))
(it's not using the image's gallery_id in the query), and then I've tried adding
has_many :bookmarked_tags, :through => :bookmarked_images, :source => :tags
to User, which causes User.find(1).bookmarked_tags to result in
ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid
source reflection macro :has_many :through for has_many
:bookmarked_tags, :through => :bookmarked_images. Use :source to
specify the source reflection.
So: how can I get Rails to run the query I posted with User.find(1).bookmarked_tags?
there are two solutions
First Solution:
Create a view inside your database that acts like a join table:
CREATE VIEW user_bookmarks_tags (
SELECT
bookmarks.user_id AS user_id
gallery_tags.tag_id AS tag_id
FROM
gallery_tags
INNER JOIN images ON gallery_tags.gallery_id = images.gallery_id
INNER JOIN bookmarks ON images.id = bookmarks.image_id
GROUP BY
user_id, tag_id
)
(you can do this inside a migration)
This view acts like a "table" with the columns user_id | tag_id
Now we can adapt our models using has_and_belongs_to_many:
user.rb
class User < ActiveRecord::Base
has_many :bookmarks
has_many :galleries
# we use our magic (view) join table here!
has_and_belongs_to_many :tags, :join_table => :user_bookmarks_tags
end
tag.rb
class Tag < ActiveRecord::Base
# we use our magic (view) join table here
has_and_belongs_to_many :users, :join_table => :user_bookmarks_tags
end
Now you can do: User.find(1).tags or Tag.find(1).users :)
Second Solution
Do the join manually without a view:
define the missing relations (needed for the automated joins foreign_key lookup):
tag.rb
class Tag < ActiveRecord::Base
has_many :gallery_tags
end
gallery_tag.rb
class GalleryTag < ActiveRecord::Base
belongs_to :gallery
belongs_to :tag
# new:
has_many :images, :through => :gallery
end
Now we can add a bookmarked_tags method to our user.rb
class User < ActiveRecordBase
has_many :bookmarks
has_many :galleries
def bookmarked_tags
Tag.joins(:gallery_tags => {:images => :bookmarks}).where('bookmarks.user_id = ?', self.id).group('tags.id')
end
end
I have 2 tables I need to join but the user_id column value is not the same in both tables. So I want to do something like this:
In my controller 4 will be substituted with current_user.id
select * from sites join pickups on sites.id = pickups.site_id where sites.user_id = '4'
But using an ActiveRecord Find.
Here are my associations:
class Site < ActiveRecord::Base
belongs_to :user
has_many :pickups
class Pickup < ActiveRecord::Base
belongs_to :site
belongs_to :user
class User < ActiveRecord::Base
has_one :profile
has_many :pickups
has_many :sites
Thanks in advance!
If you add this to your user model:
has_many :site_pickups, :through => :sites, :source => :pickups
You can do
current_user.site_pickups
try this:
sites = current_user.sites.find(:all, :include => [:pickup])