Rails 3 - Queries with sub models and count statements
I'm trying to figure out a way to eliminate a bunch of queries that seem unnecessary. Here is the situation.
model Post has_many Comments
SQL (0.1ms) SELECT COUNT(*) FROM "comments" WHERE ("comments".post_id = 5)
CACHE (0.0ms) SELECT COUNT(*) FROM "comments" WHERE ("comments".post_id = 5) CACHE (0.0ms) SELECT COUNT(*) FROM "comments" WHERE ("comments".post_id = 5) CACHE (0.0ms) SELECT COUNT(*) FROM "comments" WHERE ("comments".post_id = 5)So i'm not sure why these are running but in my view I'm checking to see if there are comments for each of the posts
<% if post.comments.count > 0 %> <!-- tried count, size, blank? -->
<table class="list">
<% post.comments.each do |animal| %>
<tr><!-- stuff here --></tr>
<% end %>
</table>
<% else %>
<h3>No comments</h3>
<% end %>
If there is a better way to do this check i'm all for change. I looked at counter_cache but looks like it is only for belongs_to relationships and 开发者_运维问答also I'm using subdomains so not sure counter_cache would work for me or not.
Any suggestions welcome
The problem here is that you are probably not eagerly loading comments for each post. Wherever you are setting the post variable in your controller, you probably have something like this:
post = Post.find(params[:id])
Try changing it to this:
post = Post.includes(:comments).find(params[:id])
Includes will automatically change your Post load query to join on comments and prepopulate post.comments
with the array of all that post's comments. Then when you do post.comments.size
you will be asking for the length of the array (instead of asking activerecord to count how many there are).
Ummm... #includes is a class method, you can't call it on the instance returned by Post.find(params[:id])
It's correct in principle, though, the syntax is Post.includes(:comments).find(params[:id])
In response to your comment:
OK, #includes is a class method for ActiveRecord::Base and its children (your models), but an instance method for ActiveRelation.
@posts = @current_user.posts.all(:include => [:comments]).all
is definitely deprecated... you want to do the inclusion before you call #all b/c in Rails 3, @current_user.posts
will return an ActiveRelation object, whereas @current_user.posts.all is going to actually load the collection of Post objects. So I would change it to:
@posts = @current_user.posts.includes(:comments).all
You shouldn't even really need to call #all, to be honest, but see which works better for you.
精彩评论