aboutsummaryrefslogtreecommitdiffstats
path: root/railties/guides/source/active_record_querying.textile
diff options
context:
space:
mode:
Diffstat (limited to 'railties/guides/source/active_record_querying.textile')
-rw-r--r--railties/guides/source/active_record_querying.textile319
1 files changed, 197 insertions, 122 deletions
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 53095a2bd3..009d541106 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -19,8 +19,6 @@ Code examples throughout this guide will refer to one or more of the following m
TIP: All of the following models use +id+ as the primary key, unless specified otherwise.
-<br />
-
<ruby>
class Client < ActiveRecord::Base
has_one :address
@@ -65,6 +63,7 @@ The methods are:
* +lock+
* +readonly+
* +from+
+* +having+
All of the above methods return an instance of <tt>ActiveRecord::Relation</tt>.
@@ -103,7 +102,7 @@ h5. +first+
<ruby>
client = Client.first
-=> #<Client id: 1, first_name: => "Lifo">
+=> #<Client id: 1, first_name: "Lifo">
</ruby>
SQL equivalent of the above is:
@@ -120,7 +119,7 @@ h5. +last+
<ruby>
client = Client.last
-=> #<Client id: 221, first_name: => "Russel">
+=> #<Client id: 221, first_name: "Russel">
</ruby>
SQL equivalent of the above is:
@@ -149,24 +148,24 @@ SQL equivalent of the above is:
SELECT * FROM clients WHERE (clients.id IN (1,10))
</sql>
-<tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
+WARNING: <tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
h4. Retrieving Multiple Objects in Batches
-Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc.
+Sometimes you need to iterate over a large set of records. For example to send a newsletter to all users, to export some data, etc.
The following may seem very straight forward at first:
<ruby>
# Very inefficient when users table has thousands of rows.
-User.each do |user|
+User.all.each do |user|
NewsLetter.weekly_deliver(user)
end
</ruby>
But if the total number of rows in the table is very large, the above approach may vary from being under performant to just plain impossible.
-This is because +User.each+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
+This is because +User.all.each+ makes Active Record fetch _the entire table_, build a model object per row, and keep the entire array in the memory. Sometimes that is just too many objects and demands too much memory.
h5. +find_each+
@@ -231,10 +230,10 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
h4. Array Conditions
-Now what if that number could vary, say as an argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
+Now what if that number could vary, say as an argument from somewhere? The find then becomes something like:
<ruby>
-Client.where(["orders_count = ?", params[:orders]])
+Client.where("orders_count = ?", params[:orders])
</ruby>
Active Record will go through the first element in the conditions value and any additional elements will replace the question marks +(?)+ in the first element.
@@ -242,7 +241,7 @@ Active Record will go through the first element in the conditions value and any
Or if you want to specify two conditions, you can do it like:
<ruby>
-Client.where(["orders_count = ? AND locked = ?", params[:orders], false])
+Client.where("orders_count = ? AND locked = ?", params[:orders], false)
</ruby>
In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
@@ -250,7 +249,7 @@ In this example, the first question mark will be replaced with the value in +par
The reason for doing code like:
<ruby>
-Client.where(["orders_count = ?", params[:orders]])
+Client.where("orders_count = ?", params[:orders])
</ruby>
instead of:
@@ -268,8 +267,8 @@ h5. Placeholder Conditions
Similar to the +(?)+ replacement style of params, you can also specify keys/values hash in your array conditions:
<ruby>
-Client.where(
- ["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
+Client.where("created_at >= :start_date AND created_at <= :end_date",
+ {:start_date => params[:start_date], :end_date => params[:end_date]})
</ruby>
This makes for clearer readability if you have a large number of variable conditions.
@@ -279,62 +278,15 @@ h5(#array-range_conditions). Range Conditions
If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
<ruby>
-Client.where(["created_at IN (?)",
- (params[:start_date].to_date)..(params[:end_date].to_date)])
+Client.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date))
</ruby>
-This would generate the proper query which is great for small ranges but not so good for larger ranges. For example if you pass in a range of date objects spanning a year that's 365 (or possibly 366, depending on the year) strings it will attempt to match your field against.
+This query will generate something similar to the following SQL:
<sql>
-SELECT * FROM users WHERE (created_at IN
- ('2007-12-31','2008-01-01','2008-01-02','2008-01-03','2008-01-04','2008-01-05',
- '2008-01-06','2008-01-07','2008-01-08','2008-01-09','2008-01-10','2008-01-11',
- '2008-01-12','2008-01-13','2008-01-14','2008-01-15','2008-01-16','2008-01-17',
- '2008-01-18','2008-01-19','2008-01-20','2008-01-21','2008-01-22','2008-01-23',...
- ‘2008-12-15','2008-12-16','2008-12-17','2008-12-18','2008-12-19','2008-12-20',
- '2008-12-21','2008-12-22','2008-12-23','2008-12-24','2008-12-25','2008-12-26',
- '2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31'))
+ SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30')
</sql>
-h5. Time and Date Conditions
-
-Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range:
-
-<ruby>
-Client.where(["created_at IN (?)",
- (params[:start_date].to_date.to_time)..(params[:end_date].to_date.to_time)])
-</ruby>
-
-<sql>
-SELECT * FROM users WHERE (created_at IN
- ('2007-12-01 00:00:00', '2007-12-01 00:00:01' ...
- '2007-12-01 23:59:59', '2007-12-02 00:00:00'))
-</sql>
-
-This could possibly cause your database server to raise an unexpected error, for example MySQL will throw back this error:
-
-<shell>
-Got a packet bigger than 'max_allowed_packet' bytes: _query_
-</shell>
-
-Where _query_ is the actual query used to get that error.
-
-In this example it would be better to use greater-than and less-than operators in SQL, like so:
-
-<ruby>
-Client.where(
- ["created_at > ? AND created_at < ?", params[:start_date], params[:end_date]])
-</ruby>
-
-You can also use the greater-than-or-equal-to and less-than-or-equal-to like this:
-
-<ruby>
-Client.where(
- ["created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]])
-</ruby>
-
-Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash Conditions":#hash-conditions section later on in the guide.
-
h4. Hash Conditions
Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:
@@ -344,13 +296,13 @@ NOTE: Only equality, range and subset checking are possible with Hash conditions
h5. Equality Conditions
<ruby>
-Client.where({ :locked => true })
+Client.where(:locked => true)
</ruby>
The field name can also be a string:
<ruby>
-Client.where({ 'locked' => true })
+Client.where('locked' => true)
</ruby>
h5(#hash-range_conditions). Range Conditions
@@ -358,7 +310,7 @@ h5(#hash-range_conditions). Range Conditions
The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.
<ruby>
-Client.where({ :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
+Client.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight)
</ruby>
This will find all clients created yesterday by using a +BETWEEN+ SQL statement:
@@ -374,7 +326,7 @@ h5. Subset Conditions
If you want to find records using the +IN+ expression you can pass an array to the conditions hash:
<ruby>
-Client.where({ :orders_count => [1,3,5] })
+Client.where(:orders_count => [1,3,5])
</ruby>
This code will generate SQL like this:
@@ -383,9 +335,9 @@ This code will generate SQL like this:
SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
</sql>
-h4. Ordering
+h3. Ordering
-To retrieve records from the database in a specific order, you can specify the +:order+ option to the +find+ call.
+To retrieve records from the database in a specific order, you can use the +order+ method.
For example, if you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table:
@@ -407,7 +359,7 @@ Or ordering by multiple fields:
Client.order("orders_count ASC, created_at DESC")
</ruby>
-h4. Selecting Specific Fields
+h3. Selecting Specific Fields
By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+.
@@ -443,7 +395,7 @@ You can also call SQL functions within the select option. For example, if you wo
Client.select("DISTINCT(name)")
</ruby>
-h4. Limit and Offset
+h3. Limit and Offset
To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +limit+ and +offset+ methods on the relation.
@@ -468,10 +420,10 @@ Client.limit(5).offset(30)
will return instead a maximum of 5 clients beginning with the 31st. The SQL looks like:
<sql>
-SELECT * FROM clients LIMIT 5, 30
+SELECT * FROM clients LIMIT 5 OFFSET 30
</sql>
-h4. Group
+h3. Group
To apply a +GROUP BY+ clause to the SQL fired by the finder, you can specify the +group+ method on the find.
@@ -486,17 +438,17 @@ And this will give you a single +Order+ object for each date where there are ord
The SQL that would be executed would be something like this:
<sql>
-SELECT * FROM orders GROUP BY date(created_at)
+SELECT * FROM orders GROUP BY date(created_at) ORDER BY created_at
</sql>
-h4. Having
+h3. Having
SQL uses the +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can add the +HAVING+ clause to the SQL fired by the +Model.find+ by adding the +:having+ option to the find.
For example:
<ruby>
-Order.group("date(created_at)".having(["created_at > ?", 1.month.ago])
+Order.group("date(created_at)").having("created_at > ?", 1.month.ago)
</ruby>
The SQL that would be executed would be something like this:
@@ -507,25 +459,49 @@ SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15'
This will return single order objects for each day, but only for the last month.
-h4. Readonly Objects
+h3. Overriding Conditions
-To explicitly disallow modification/destruction of the matching records returned in a Relation object, you could chain the +readonly+ method as +true+ to the find call.
+You can specify certain conditions to be excepted by using the +except+ method.
-Any attempt to alter or destroy the readonly records will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. To set this option, specify it like this:
+For example:
<ruby>
-Client.first.readonly(true)
+Post.where('id > 10').limit(20).order('id asc').except(:order)
</ruby>
-For example, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception:
+The SQL that would be executed:
+
+<sql>
+SELECT * FROM posts WHERE id > 10 LIMIT 20
+</sql>
+
+You can also override conditions using the +only+ method.
+
+For example:
<ruby>
-client = Client.first.readonly(true)
-client.locked = false
+Post.where('id > 10').limit(20).order('id desc').only(:order, :where)
+</ruby>
+
+The SQL that would be executed:
+
+<sql>
+SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
+</sql>
+
+h3. Readonly Objects
+
+Active Record provides +readonly+ method on a relation to explicitly disallow modification or deletion of any of the returned object. Any attempt to alter or destroy a readonly record will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception.
+
+<ruby>
+client = Client.readonly.first
+client.visits += 1
client.save
</ruby>
-h4. Locking Records for Update
+As +client+ is explicitly set to be a readonly object, the above code will raise an +ActiveRecord::ReadOnlyRecord+ exception when calling +client.save+ with an updated value of _visits_.
+
+h3. Locking Records for Update
Locking is helpful for preventing race conditions when updating records in the database and ensuring atomic updates.
@@ -534,7 +510,7 @@ Active Record provides two locking mechanisms:
* Optimistic Locking
* Pessimistic Locking
-h5. Optimistic Locking
+h4. Optimistic Locking
Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened. An +ActiveRecord::StaleObjectError+ exception is thrown if that has occurred and the update is ignored.
@@ -569,15 +545,15 @@ class Client < ActiveRecord::Base
end
</ruby>
-h5. Pessimistic Locking
+h4. Pessimistic Locking
-Pessimistic locking uses a locking mechanism provided by the underlying database. Passing +:lock => true+ to +Model.find+ obtains an exclusive lock on the selected rows. +Model.find+ using +:lock+ are usually wrapped inside a transaction for preventing deadlock conditions.
+Pessimistic locking uses a locking mechanism provided by the underlying database. Using +lock+ when building a relation obtains an exclusive lock on the selected rows. Relations using +lock+ are usually wrapped inside a transaction for preventing deadlock conditions.
For example:
<ruby>
Item.transaction do
- i = Item.first(:lock => true)
+ i = Item.lock.first
i.name = 'Jones'
i.save
end
@@ -592,25 +568,25 @@ Item Update (0.4ms) UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `
SQL (0.8ms) COMMIT
</sql>
-You can also pass raw SQL to the +:lock+ option to allow different types of locks. For example, MySQL has an expression called +LOCK IN SHARE MODE+ where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:
+You can also pass raw SQL to the +lock+ method for allowing different types of locks. For example, MySQL has an expression called +LOCK IN SHARE MODE+ where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:
<ruby>
Item.transaction do
- i = Item.find(1, :lock => "LOCK IN SHARE MODE")
+ i = Item.lock("LOCK IN SHARE MODE").find(1)
i.increment!(:views)
end
</ruby>
h3. Joining Tables
-<tt>Model.find</tt> provides a +:joins+ option for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to specify the +:joins+ option:
+Active Record provides a finder method called +joins+ for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to use the +joins+ method.
h4. Using a String SQL Fragment
-You can just supply the raw SQL specifying the +JOIN+ clause to the +:joins+ option. For example:
+You can just supply the raw SQL specifying the +JOIN+ clause to +joins+:
<ruby>
-Client.all(:joins => 'LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
+Client.joins('LEFT OUTER JOIN addresses ON addresses.client_id = clients.id')
</ruby>
This will result in the following SQL:
@@ -621,11 +597,9 @@ SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id =
h4. Using Array/Hash of Named Associations
-WARNING: This method only works with +INNER JOIN+,
+WARNING: This method only works with +INNER JOIN+.
-<br />
-
-Active Record lets you use the names of the "associations":association_basics.html defined on the model as a shortcut for specifying the +:joins+ option.
+Active Record lets you use the names of the "associations":association_basics.html defined on the model as a shortcut for specifying +JOIN+ clause for those associations when using the +joins+ method.
For example, consider the following +Category+, +Post+, +Comments+ and +Guest+ models:
@@ -674,7 +648,7 @@ Post.joins(:category, :comments)
This produces:
<sql>
-SELECT posts.* FROM posts
+SELECT posts.* FROM posts
INNER JOIN categories ON posts.category_id = categories.id
INNER JOIN comments ON comments.post_id = posts.id
</sql>
@@ -718,7 +692,7 @@ Eager loading is the mechanism for loading the associated records of the objects
Consider the following code, which finds 10 clients and prints their postcodes:
<ruby>
-clients = Client.all(:limit => 10)
+clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
@@ -753,7 +727,7 @@ h4. Eager Loading Multiple Associations
Active Record lets you eager load any number of associations with a single +Model.find+ call by using an array, hash, or a nested hash of array/hash with the +includes+ method.
-h5. Array of Multiple Associations
+h5. Array of Multiple Associations
<ruby>
Post.includes(:category, :comments)
@@ -771,17 +745,119 @@ This will find the category with id 1 and eager load all of the associated posts
h4. Specifying Conditions on Eager Loaded Associations
-Even though Active Record lets you specify conditions on the eager loaded associations just like +joins+, the recommended way is to use "joins":#joining-tables instead.
+Even though Active Record lets you specify conditions on the eager loaded associations just like +joins+, the recommended way is to use "joins":#joining-tables instead.
+
+However if you must do this, you may use +where+ as you would normally.
+
+<ruby>
+Post.includes(:comments).where("comments.visible", true)
+</ruby>
+
+This would generate a query which contains a +LEFT OUTER JOIN+ whereas the +joins+ method would generate one using the +INNER JOIN+ function instead.
+
+<ruby>
+ SELECT "posts"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id" WHERE (comments.visible)
+</ruby>
+
+If there was no +where+ condition, this would generate the normal set of two queries.
+
+If, in the case of this +includes+ query, there were no comments for any posts, all the posts would still be loaded. By using +joins+ (an INNER JOIN), the join conditions *must* match, otherwise no records will be returned.
+
+h3. Scopes
+
+Scoping allows you to specify commonly-used ARel queries which can be referenced as method calls on the association objects or models. With these scopes, you can use every method previously covered such as +where+, +joins+ and +includes+. All scope methods will return an +ActiveRecord::Relation+ object which will allow for further methods (such as other scopes) to be called on it.
+
+To define a simple scope, we use the +scope+ method inside the class, passing the ARel query that we'd like run when this scope is called:
+
+<ruby>
+class Post < ActiveRecord::Base
+ scope :published, where(:published => true)
+end
+</ruby>
+
+Just like before, these methods are also chainable:
+
+<ruby>
+class Post < ActiveRecord::Base
+ scope :published, where(:published => true).joins(:category)
+end
+</ruby>
+
+Scopes are also chainable within scopes:
+
+<ruby>
+class Post < ActiveRecord::Base
+ scope :published, where(:published => true)
+ scope :published_and_commented, published.and(self.arel_table[:comments_count].gt(0))
+end
+</ruby>
+
+To call this +published+ scope we can call it on either the class:
+
+<ruby>
+Post.published => [published posts]
+</ruby>
+
+Or on an association consisting of +Post+ objects:
+
+<ruby>
+category = Category.first
+category.posts.published => [published posts belonging to this category]
+</ruby>
+
+h4. Working with times
+
+If you're working with dates or times within scopes, due to how they are evaluated, you will need to use a lambda so that the scope is evaluated every time.
+
+<ruby>
+class Post < ActiveRecord::Base
+ scope :last_week, lambda { where("created_at < ?", Time.zone.now ) }
+end
+</ruby>
+
+Without the +lambda+, this +Time.zone.now+ will only be called once.
+
+h4. Passing in arguments
+
+When a +lambda+ is used for a +scope+, it can take arguments:
+
+<ruby>
+class Post < ActiveRecord::Base
+ scope :1_week_before, lambda { |time| where("created_at < ?", time)
+end
+</ruby>
+
+This may then be called using this:
+
+<ruby>
+Post.1_week_before(Time.zone.now)
+</ruby>
+
+However, this is just duplicating the functionality that would be provided to you by a class method.
+
+<ruby>
+class Post < ActiveRecord::Base
+ def self.1_week_before(time)
+ where("created_at < ?", time)
+ end
+end
+</ruby>
+
+Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
+
+<ruby>
+category.posts.1_week_before(time)
+</ruby>
h3. Dynamic Finders
-For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have also have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+.
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+ methods.
-You can do +find_last_by_*+ methods too which will find the last record matching your argument.
+You can also use +find_last_by_*+ methods which will find the last record matching your argument.
You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+
-If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_first_name_and_locked("Ryan", true)+.
+If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields. For example, +Client.find_by_first_name_and_locked("Ryan", true)+.
There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_first_name(params[:first_name])+. Using this will first perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+:
@@ -807,8 +883,8 @@ h3. Finding by SQL
If you'd like to use your own SQL to find records in a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even if the underlying query returns just a single record. For example you could run this query:
<ruby>
-Client.find_by_sql("SELECT * FROM clients
- INNER JOIN orders ON clients.id = orders.client_id
+Client.find_by_sql("SELECT * FROM clients
+ INNER JOIN orders ON clients.id = orders.client_id
ORDER clients.created_at desc")
</ruby>
@@ -838,13 +914,13 @@ Client.exists?(1,2,3)
Client.exists?([1,2,3])
</ruby>
-The +exists+ method may also take a +conditions+ option much like find:
+It's even possible to use +exists?+ without any arguments on a model or a relation.
<ruby>
-Client.exists?(:conditions => "first_name = 'Ryan'")
+Client.where(:first_name => 'Ryan').exists?
</ruby>
-It's even possible to use +exists?+ without any arguments:
+The above returns +true+ if there is at least one client with the +first_name+ 'Ryan' and +false+ otherwise.
<ruby>
Client.exists?
@@ -856,22 +932,24 @@ h3. Calculations
This section uses count as an example method in this preamble, but the options described apply to all sub-sections.
-<tt>count</tt> takes conditions much in the same way +exists?+ does:
+All calculation methods work directly on a model:
<ruby>
-Client.count(:conditions => "first_name = 'Ryan'")
+Client.count
+# SELECT count(*) AS count_all FROM clients
</ruby>
-Which will execute:
+Or on a relation :
-<sql>
-SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
-</sql>
+<ruby>
+Client.where(:first_name => 'Ryan').count
+# SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
+</ruby>
-You can also use the +includes+ or +joins+ methods for this to do something a little more complex:
+You can also use various finder methods on a relation for performing complex calculations:
<ruby>
-Client.where("clients.first_name = 'Ryan' AND orders.status = 'received'").includes("orders").count
+Client.includes("orders").where(:first_name => 'Ryan', :orders => {:status => 'received'}).count
</ruby>
Which will execute:
@@ -882,8 +960,6 @@ SELECT count(DISTINCT clients.id) AS count_all FROM clients
(clients.first_name = 'Ryan' AND orders.status = 'received')
</sql>
-This code specifies +clients.first_name+ just in case one of the join tables has a field also called +first_name+ and it uses +orders.status+ because that's the name of our join table.
-
h4. Count
If you want to see how many records are in your model's table you could call +Client.count+ and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use +Client.count(:age)+.
@@ -934,8 +1010,7 @@ For options, please see the parent section, "Calculations":#calculations.
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16
-
+* December 23 2010: Add documentation for the +scope+ method. "Ryan Bigg":http://ryanbigg.com
* April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
* February 7, 2009: Second version by "Pratik":credits.html#lifo