Reputation: 1049
I have a rails app with many following pieces of code:
Our active community of <%= Account.find_all_by_admin(false).count %>
My question is is this the right way to do counts on views? It seems so "dirty" is there a more railish, way to do counts? I'm thinking named scopes perhaps, but I just want to be sure that these type of things won't have a greater impact in performance.
Thank You,
Upvotes: 14
Views: 32061
Reputation: 2610
You should create an instance variable
in the controller and use it in the view.
This will make you view clear.
@accounts_count = Account.where(admin: false).count
And use it in the view like `<%= @accounts_count %>
If you want to loop though the results along with the count, you can use the following way:
@accounts = Account.where(admin: false)
in view, write like below:
<%= @accounts.count %>
// For looping account
<% @accounts.each do |account| %>
# do some stuff
<% end %>
But the above way will fire 2 queries, 1 for count and other for the loop.
Correct Approach
Use size
instead of count
. It will fire 1 query but you need to change the order of count in your view like:
// For looping account
<% @accounts.each do |account| %>
# do some stuff
<% end %>
<%= @accounts.size %>
If you want the same order, then load the user object first like below
<%= @accounts.load.size %>
// For looping account
<% @accounts.each do |account| %>
# do some stuff
<% end %>
Upvotes: -1
Reputation: 8104
I would recommend you to avoid direct access to database in my templates because then you're losing a bit of flexibility when it comes to caching.
Try to prepare all the data you need to render in your action instead and then use meaningful instance variables like @number_of_accounts
or @accounts.count
.
This will make your views cleaner and easier to debug and also a bit more DRY if you render action in different formats (html, json, etc)
As to how do you get your numbers - it doesn't really matter that much, just move away from find_* methods towards scoping and write readable code
Upvotes: 8
Reputation: 3213
You can use following query instead of Account.where(:admin => false).count
Account.select(:id).where(:admin => false).count
Just select one column, instead of selecting all. It generates the following query and it is faster than the previous one:
SELECT COUNT("accounts"."id") FROM "accounts" where admin = false
Upvotes: 0
Reputation: 25994
A named scope shouldn't have an impact on performance
scope :not_admin, where(:admin => false)
Then you can have Account.not_admin.count
Edited per DGM's comment: To check the generated SQL in a console, compare Account.not_admin.to_sql
with Account.find_all_by_admin(false).to_sql
Upvotes: 2
Reputation: 176562
You don't need a name scope to perform a count.
Account.where(:admin => false).count
But named scopes are an excellent way to make your code more reusable.
Named scopes don't have any noticeable performance impact on your application.
Upvotes: 17
Reputation: 14679
In rails 3 a simple call to count issues a simple count request:
Contact.count
is resolved as:
SELECT COUNT(*) AS count_id FROM "contacts"
a find all by field name will resolve as:
Contact.find_all_by_country("Canada")
SELECT "contacts".* FROM "contacts" WHERE ("contacts"."country" = 'Canada')
I would recommend indexing your admin column for faster lookups and this can be translated into a named scope, but that by itself will only predefine the query, not optimize it.
It is important to note that if you issue
Contact.find_all_by_country("Canada").count
count
is a method on the array class and doesn't actually issue a count on the database:
Contact.find_all_by_country("Canada").count
SELECT "contacts".* FROM "contacts" WHERE ("contacts"."country" = 'Canada')
Upvotes: 3