4
\$\begingroup\$

I have three models which look like this:

class Post < ActiveRecord::Base
 belongs_to :user, :counter_cache => :count_post
 has_many :likes
end
class User < ActiveRecord::Base
 has_many :posts
 has_many :likes
end
class Like < ActiveRecord::Base
 belongs_to :user
 belongs_to :post, :counter_cache => :count_like
end

A user can only like once per Post so I have a method that checks if the current user already like a post and the dislike link is displayed.

<% if post.has_like_from? current_user %>
 <li id="like_<%= dom_id(post) %>"><%= link_to "like", create_like_path(:post_id => post.id), :remote => true %></li>
<% else %>
 <li id="dislike_<%= dom_id(post) %>"><%= render 'dislike', :post => post %></li>
<% end %>

And I have this method on model post:

 def has_like_from?(target_user)
 likes.where(:user_id => target_user.id).first == nil
 end

Is there any way to check every post if current user already like a post? I want to increase performance because load to many posts per request.

Example: startrace when load 7 records

Started GET "/" for 127.0.0.1 at 2014年06月30日 23:32:51 +0700
 ←[1m←[35mUser Load (1.0ms)←[0m SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
Processing by HomesController#index as HTML
 ←[1m←[36mPost Load (1.0ms)←[0m ←[1mSELECT "posts".* FROM "posts" ORDER BY created_at DESC←[0m
 ←[1m←[35mLike Load (1.0ms)←[0m SELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 7 AND "lik
es"."user_id" = 1 LIMIT 1
 ←[1m←[36mLike Load (0.0ms)←[0m ←[1mSELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 6 AND
"likes"."user_id" = 1 LIMIT 1←[0m
 ←[1m←[35mLike Load (1.0ms)←[0m SELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 5 AND "lik
es"."user_id" = 1 LIMIT 1
 ←[1m←[36mLike Load (1.0ms)←[0m ←[1mSELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 4 AND
"likes"."user_id" = 1 LIMIT 1←[0m
 ←[1m←[35mLike Load (0.0ms)←[0m SELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 3 AND "lik
es"."user_id" = 1 LIMIT 1
 ←[1m←[36mLike Load (0.0ms)←[0m ←[1mSELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 2 AND
"likes"."user_id" = 1 LIMIT 1←[0m
 ←[1m←[35mLike Load (0.0ms)←[0m SELECT "likes".* FROM "likes" WHERE "likes"."post_id" = 1 AND "lik
es"."user_id" = 1 LIMIT 1
 Rendered homes/_posts.html.erb (29.0ms)
asked Jun 30, 2014 at 16:57
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Have you created an index for likes.post_id? Note also that you can simplify that method by using has_many with the option through and ActiveRecord#include?. That's what I'd write (with positive logic, I don't see why has_like_from? should perform a == nil):

class Post < ActiveRecord::Base
 belongs_to :user, :counter_cache => :count_post
 has_many :likes
 has_many :like_users, :through => :likes
 def liked_by?(user)
 like_users.include?(user)
 end
end
answered Jun 30, 2014 at 18:59
\$\endgroup\$
2
  • \$\begingroup\$ i think this will load whole like_users table to memory, it may be more efficient to do like_users.exists?(user_id: user.id). \$\endgroup\$ Commented Jul 24, 2014 at 9:38
  • \$\begingroup\$ @rui, Here #include? comes from active-record, not Array, it should generate the same SQL than your code. I cannot test right now, but I'd be surprised if that's not the case. \$\endgroup\$ Commented Jul 24, 2014 at 9:39

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.