diff options
Diffstat (limited to 'guides/source/active_record_querying.textile')
-rw-r--r-- | guides/source/active_record_querying.textile | 101 |
1 files changed, 59 insertions, 42 deletions
diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 8e23a577e2..902ceeb78b 100644 --- a/guides/source/active_record_querying.textile +++ b/guides/source/active_record_querying.textile @@ -133,6 +133,24 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 <tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised. +h5. +find_by+ + +<tt>Model.find_by</tt> finds the first record matching some conditions. For example: + +<ruby> +Client.find_by first_name: 'Lifo' +# => #<Client id: 1, first_name: "Lifo"> + +Client.find_by first_name: 'Jon' +# => nil +</ruby> + +It is equivalent to writing: + +<ruby> +Client.where(first_name: 'Lifo').first +</ruby> + h5(#first_1). +first!+ <tt>Model.first!</tt> finds the first record. For example: @@ -167,6 +185,24 @@ SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1 <tt>Model.last!</tt> raises +RecordNotFound+ if no matching record is found. +h5(#find_by_1). +find_by!+ + +<tt>Model.find_by!</tt> finds the first record matching some conditions. It raises +RecordNotFound+ if no matching record is found. For example: + +<ruby> +Client.find_by! first_name: 'Lifo' +# => #<Client id: 1, first_name: "Lifo"> + +Client.find_by! first_name: 'Jon' +# => RecordNotFound +</ruby> + +It is equivalent to writing: + +<ruby> +Client.where(first_name: 'Lifo').first! +</ruby> + h4. Retrieving Multiple Objects h5. Using Multiple Primary Keys @@ -320,20 +356,6 @@ Client.where("created_at >= :start_date AND created_at <= :end_date", This makes for clearer readability if you have a large number of variable conditions. -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 => (params[:start_date].to_date)..(params[:end_date].to_date)) -</ruby> - -This query will generate something similar to the following SQL: - -<sql> - SELECT "clients".* FROM "clients" WHERE ("clients"."created_at" BETWEEN '2010-09-29' AND '2010-11-30') -</sql> - 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: @@ -352,9 +374,9 @@ The field name can also be a string: Client.where('locked' => true) </ruby> -h5(#hash-range_conditions). Range Conditions +NOTE: The values cannot be symbols. For example, you cannot do +Client.where(:status => :active)+. -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. +h5(#hash-range_conditions). Range Conditions <ruby> Client.where(:created_at => (Time.now.midnight - 1.day)..Time.now.midnight) @@ -503,7 +525,9 @@ 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 date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) +SELECT date(created_at) as ordered_date, sum(price) as total_price +FROM orders +GROUP BY date(created_at) </sql> h3. Having @@ -519,7 +543,10 @@ Order.select("date(created_at) as ordered_date, sum(price) as total_price").grou The SQL that would be executed would be something like this: <sql> -SELECT date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at) HAVING sum(price) > 100 +SELECT date(created_at) as ordered_date, sum(price) as total_price +FROM orders +GROUP BY date(created_at) +HAVING sum(price) > 100 </sql> This will return single order objects for each day, but only those that are ordered more than $100 in a day. @@ -659,7 +686,7 @@ Optimistic locking allows multiple users to access the same record for edits, an <strong>Optimistic locking column</strong> -In order to use optimistic locking, the table needs to have a column called +lock_version+. Each time the record is updated, Active Record increments the +lock_version+ column. If an update request is made with a lower value in the +lock_version+ field than is currently in the +lock_version+ column in the database, the update request will fail with an +ActiveRecord::StaleObjectError+. Example: +In order to use optimistic locking, the table needs to have a column called +lock_version+ of type integer. Each time the record is updated, Active Record increments the +lock_version+ column. If an update request is made with a lower value in the +lock_version+ field than is currently in the +lock_version+ column in the database, the update request will fail with an +ActiveRecord::StaleObjectError+. Example: <ruby> c1 = Client.find(1) @@ -793,7 +820,7 @@ SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id </sql> -Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:post).select("distinct(categories.id)"). +Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use Category.joins(:posts).select("distinct(categories.id)"). h5. Joining Multiple Associations @@ -943,21 +970,23 @@ If, in the case of this +includes+ query, there were no comments for any posts, 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. +Scoping allows you to specify commonly-used 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: +To define a simple scope, we use the +scope+ method inside the class, passing the query that we'd like run when this scope is called: <ruby> class Post < ActiveRecord::Base - scope :published, where(:published => true) + scope :published, -> { where(published: true) } end </ruby> -Just like before, these methods are also chainable: +This is exactly the same as defining a class method, and which you use is a matter of personal preference: <ruby> class Post < ActiveRecord::Base - scope :published, where(:published => true).joins(:category) + def self.published + where(published: true) + end end </ruby> @@ -965,8 +994,8 @@ 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)) + scope :published, -> { where(:published => true) } + scope :published_and_commented, -> { published.where("comments_count > 0") } end </ruby> @@ -983,25 +1012,13 @@ 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 :created_before_now, 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: +Your scope can take arguments: <ruby> class Post < ActiveRecord::Base - scope :created_before, lambda { |time| where("created_at < ?", time) } + scope :created_before, ->(time) { where("created_at < ?", time) } end </ruby> @@ -1048,7 +1065,7 @@ If we wish for a scope to be applied across all queries to the model we can use <ruby> class Client < ActiveRecord::Base - default_scope where("removed_at IS NULL") + default_scope { where("removed_at IS NULL") } end </ruby> |