diff options
Diffstat (limited to 'guides/source/active_record_querying.textile')
-rw-r--r-- | guides/source/active_record_querying.textile | 87 |
1 files changed, 60 insertions, 27 deletions
diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile index 8e23a577e2..a0517eea1d 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 @@ -352,6 +388,8 @@ The field name can also be a string: Client.where('locked' => true) </ruby> +NOTE: The values cannot be symbols. For example, you cannot do +Client.where(:status => :active)+. + 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. @@ -503,7 +541,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 +559,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 +702,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 +836,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 @@ -883,7 +926,7 @@ This code looks fine at the first sight. But the problem lies within the total n Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the +includes+ method of the +Model.find+ call. With +includes+, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries. -Revisiting the above case, we could rewrite +Client.all+ to use eager load addresses: +Revisiting the above case, we could rewrite +Client.limit(10)+ to use eager load addresses: <ruby> clients = Client.includes(:address).limit(10) @@ -943,21 +986,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 +1010,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 +1028,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 +1081,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> |