Permalink
Browse files

Fixed all points in http://rails.lighthouseapp.com/projects/16213-rai…

  • Loading branch information...
1 parent 70a983a commit 2b94b3437e819fbaffb7d5b7cd158efdcf903566 @radar radar committed Nov 19, 2008
Showing with 26 additions and 22 deletions.
  1. +26 −22 railties/doc/guides/source/finders.txt
@@ -13,6 +13,8 @@ This guide covers the +find+ method defined in +ActiveRecord::Base+, as well as
If you're used to using raw SQL to find database records, you'll find that there are generally better ways to carry out the same operations in Rails. Active Record insulates you from the need to use SQL in most cases.
+The SQL in your log may have some quoting, and that quoting depends on the backend (MySQL, for example, puts backticks around field and table names). Attempting to copy the raw SQL contained within this guide may not work in your database system. Please consult the database systems manual before attempting to execute any SQL.
+
== The Sample Models
This guide demonstrates finding using the following models:
@@ -52,16 +54,16 @@ Active Record will perform queries on the database for you and is compatible wit
[source, sql]
-------------------------------------------------------
-SELECT * FROM +clients+ WHERE (+clients+.+id+ = 1)
+SELECT * FROM clients WHERE (clients.id = 1)
-------------------------------------------------------
-NOTE: Because this is a standard table created from a migration in Rail, the primary key is defaulted to 'id'. If you have specified a different primary key in your migrations, this is what Rails will find on when you call the find method, not the id column.
+NOTE: Because this is a standard table created from a migration in Rails, the primary key is defaulted to 'id'. If you have specified a different primary key in your migrations, this is what Rails will find on when you call the find method, not the id column.
If you wanted to find clients with id 1 or 2, you call +Client.find([1,2])+ or +Client.find(1,2)+ and then this will be executed as:
[source, sql]
-------------------------------------------------------
-SELECT * FROM +clients+ WHERE (+clients+.+id+ IN (1,2))
+SELECT * FROM clients WHERE (clients.id IN (1,2))
-------------------------------------------------------
-------------------------------------------------------
@@ -76,15 +78,15 @@ Note that if you pass in a list of numbers that the result will be returned as a
NOTE: If +find(id)+ or +find([id1, id2])+ fails to find any records, it will raise a +RecordNotFound+ exception.
-If you wanted to find the first client you would simply type +Client.first+ and that would find the first client created in your clients table:
+If you wanted to find the first Client object you would simply type +Client.first+ and that would find the first client created in your clients table:
-------------------------------------------------------
>> Client.first
=> #<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">
-------------------------------------------------------
-If you were running script/server you might see the following output:
+If you were reading your log file (the default is log/development.log) you may see something like this:
[source,sql]
-------------------------------------------------------
@@ -93,10 +95,10 @@ SELECT * FROM clients LIMIT 1
Indicating the query that Rails has performed on your database.
-To find the last client you would simply type +Client.find(:last)+ and that would find the last client created in your clients table:
+To find the last Client object you would simply type +Client.find(:last)+ and that would find the last client created in your clients table:
-------------------------------------------------------
->> Client.find(:last)
+>> Client.last
=> #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">
-------------------------------------------------------
@@ -106,7 +108,7 @@ To find the last client you would simply type +Client.find(:last)+ and that woul
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
-------------------------------------------------------
-To find all the clients you would simply type +Client.all+ and that would find all the clients in your clients table:
+To find all the Client objects you would simply type +Client.all+ and that would find all the clients in your clients table:
-------------------------------------------------------
>> Client.all
@@ -116,9 +118,9 @@ To find all the clients you would simply type +Client.all+ and that would find a
created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
-------------------------------------------------------
-As alternatives to calling +Client.first+, +Client.last+, and +Client.all+, you can use the class methods +Client.first+, +Client.last+, and +Client.all+ instead. +Client.first+, +Client.last+ and +Client.all+ just call their longer counterparts: +Client.find(:first)+, +Client.find(:last)+ and +Client.find(:all)+ respectively.
+You may see in Rails code that there are calls to methods such as +Client.find(:all)+, +Client.find(:first)+ and +Client.find(:last)+. These methods are just alternatives to +Client.all+, +Client.first+ and +Client.last+ respectively.
-Be aware that +Client.first+/+Client.find(:first)+ and +Client.last+/+Client.find(:last)+ will both return a single object, where as +Client.all+/+Client.find(:all)+ will return an array of Client objects, just as passing in an array of ids to find will do also.
+Be aware that +Client.first+/+Client.find(:first)+ and +Client.last+/+Client.find(:last)+ will both return a single object, where as +Client.all+/+Client.find(:all)+ will return an array of Client objects, just as passing in an array of ids to +find+ will do also.
== Conditions
@@ -132,19 +134,20 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
=== Array Conditions ===
-Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. 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. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in params orders and the second will be replaced with true and this will find the first record in the table that has '2' as its value for the orders_count field and 'false' for its locked field.
+Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. 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. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with +false+ and this will find the first record in the table that has '2' as its value for the +orders_count+ field and +false+ for its locked field.
The reason for doing code like:
[source, ruby]
-------------------------------------------------------
-+Client.first(:conditions => ["orders_count = ?", params[:orders]])+
+Client.first(:conditions => ["orders_count = ?", params[:orders]])
-------------------------------------------------------
instead of:
+[source, ruby]
-------------------------------------------------------
-+Client.first(:conditions => "orders_count = #{params[:orders]}")+
+Client.first(:conditions => "orders_count = #{params[:orders]}")
-------------------------------------------------------
is because of parameter safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your parameters directly inside the conditions string.
@@ -163,7 +166,7 @@ This would generate the proper query which is great for small ranges but not so
[source, sql]
-------------------------------------------------------
-SELECT * FROM +users+ WHERE (created_at IN
+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',
@@ -183,7 +186,7 @@ Client.all(:conditions => ["created_at IN (?)",
[source, sql]
-------------------------------------------------------
-SELECT * FROM +users+ WHERE (created_at IN
+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'))
-------------------------------------------------------
@@ -214,7 +217,7 @@ Client.all(:conditions =>
Just like in Ruby.
-=== Hash Conditions ===
+=== Placeholder Conditions ===
Similar to the array style of params you can also specify keys in your conditions:
@@ -277,7 +280,7 @@ The SQL that would be executed would be something like this:
[source, sql]
-------------------------------------------------------
-SELECT * FROM +orders+ GROUP BY date(created_at)
+SELECT * FROM orders GROUP BY date(created_at)
-------------------------------------------------------
== Read Only
@@ -363,9 +366,9 @@ There's another set of dynamic finders that let you find or create/initialize ob
[source,sql]
-------------------------------------------------------
-SELECT * FROM +clients+ WHERE (+clients+.+name+ = 'Ryan') LIMIT 1
+SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
BEGIN
-INSERT INTO +clients+ (+name+, +updated_at+, +created_at+, +orders_count+, +locked+)
+INSERT INTO clients (name, updated_at, created_at, orders_count, locked)
VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', '0', '0')
COMMIT
-------------------------------------------------------
@@ -571,7 +574,7 @@ Which will execute:
[source, sql]
-------------------------------------------------------
-SELECT count(*) AS count_all FROM +clients+ WHERE (first_name = 1)
+SELECT count(*) AS count_all FROM clients WHERE (first_name = 1)
-------------------------------------------------------
You can also use +include+ or +joins+ for this to do something a little more complex:
@@ -585,8 +588,8 @@ Which will execute:
[source, sql]
-------------------------------------------------------
-SELECT count(DISTINCT +clients+.id) AS count_all FROM +clients+
- LEFT OUTER JOIN +orders+ ON orders.client_id = client.id WHERE
+SELECT count(DISTINCT clients.id) AS count_all FROM clients
+ LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE
(clients.first_name = 'name' AND orders.status = 'received')
-------------------------------------------------------
@@ -655,6 +658,7 @@ Thanks to Mike Gunderloy for his tips on creating this guide.
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16[Lighthouse ticket]
+* Wednesday 16 2008: Fixed all points specified in http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-11[this comment]
* November 8, 2008: Editing pass by link:../authors.html#mgunderloy[Mike Gunderloy] . First release version.
* October 27, 2008: Added scoped section, added named params for conditions and added sub-section headers for conditions section by Ryan Bigg
* October 27, 2008: Fixed up all points specified in http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-6[this comment] with an exception of the final point by Ryan Bigg

0 comments on commit 2b94b34

Please sign in to comment.