rails 4.0.0 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +30 -23
- data/guides/CHANGELOG.md +108 -6
- data/guides/Rakefile +21 -6
- data/guides/assets/images/akshaysurve.jpg +0 -0
- data/guides/assets/images/edge_badge.png +0 -0
- data/guides/assets/images/feature_tile.gif +0 -0
- data/guides/assets/images/footer_tile.gif +0 -0
- data/guides/assets/images/fxn.png +0 -0
- data/guides/assets/images/getting_started/article_with_comments.png +0 -0
- data/guides/assets/images/getting_started/challenge.png +0 -0
- data/guides/assets/images/getting_started/confirm_dialog.png +0 -0
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +0 -0
- data/guides/assets/images/getting_started/form_with_errors.png +0 -0
- data/guides/assets/images/getting_started/index_action_with_edit_link.png +0 -0
- data/guides/assets/images/getting_started/new_article.png +0 -0
- data/guides/assets/images/getting_started/rails_welcome.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_controller.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_route_matches.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_articles.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_articles_new.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_articles.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_articles.png +0 -0
- data/guides/assets/images/header_tile.gif +0 -0
- data/guides/assets/images/icons/README +1 -1
- data/guides/assets/images/icons/callouts/11.png +0 -0
- data/guides/assets/images/icons/callouts/12.png +0 -0
- data/guides/assets/images/icons/callouts/13.png +0 -0
- data/guides/assets/images/icons/callouts/15.png +0 -0
- data/guides/assets/images/icons/caution.png +0 -0
- data/guides/assets/images/icons/example.png +0 -0
- data/guides/assets/images/radar.png +0 -0
- data/guides/assets/images/rails4_features.png +0 -0
- data/guides/assets/images/rails_guides_kindle_cover.jpg +0 -0
- data/guides/assets/images/vijaydev.jpg +0 -0
- data/guides/assets/javascripts/guides.js +36 -34
- data/guides/assets/stylesheets/main.css +6 -2
- data/guides/assets/stylesheets/print.css +1 -1
- data/guides/bug_report_templates/action_controller_gem.rb +47 -0
- data/guides/bug_report_templates/action_controller_master.rb +54 -0
- data/guides/bug_report_templates/active_record_gem.rb +5 -2
- data/guides/bug_report_templates/active_record_master.rb +3 -2
- data/guides/bug_report_templates/generic_gem.rb +15 -0
- data/guides/bug_report_templates/generic_master.rb +26 -0
- data/guides/rails_guides.rb +23 -4
- data/guides/rails_guides/generator.rb +1 -1
- data/guides/rails_guides/helpers.rb +4 -2
- data/guides/rails_guides/levenshtein.rb +27 -21
- data/guides/rails_guides/markdown.rb +11 -7
- data/guides/rails_guides/markdown/renderer.rb +1 -1
- data/guides/source/2_2_release_notes.md +3 -3
- data/guides/source/2_3_release_notes.md +12 -12
- data/guides/source/3_0_release_notes.md +10 -13
- data/guides/source/3_1_release_notes.md +7 -4
- data/guides/source/3_2_release_notes.md +17 -14
- data/guides/source/4_0_release_notes.md +110 -54
- data/guides/source/4_1_release_notes.md +730 -0
- data/guides/source/4_2_release_notes.md +877 -0
- data/guides/source/_license.html.erb +1 -1
- data/guides/source/_welcome.html.erb +6 -2
- data/guides/source/action_controller_overview.md +223 -57
- data/guides/source/action_mailer_basics.md +129 -76
- data/guides/source/action_view_overview.md +247 -246
- data/guides/source/active_job_basics.md +339 -0
- data/guides/source/active_model_basics.md +374 -20
- data/guides/source/active_record_basics.md +46 -45
- data/guides/source/active_record_callbacks.md +83 -28
- data/guides/source/{migrations.md → active_record_migrations.md} +191 -275
- data/guides/source/active_record_postgresql.md +433 -0
- data/guides/source/active_record_querying.md +382 -300
- data/guides/source/active_record_validations.md +64 -55
- data/guides/source/active_support_core_extensions.md +229 -187
- data/guides/source/active_support_instrumentation.md +23 -22
- data/guides/source/api_documentation_guidelines.md +167 -15
- data/guides/source/asset_pipeline.md +768 -294
- data/guides/source/association_basics.md +188 -96
- data/guides/source/autoloading_and_reloading_constants.md +1311 -0
- data/guides/source/caching_with_rails.md +45 -11
- data/guides/source/command_line.md +96 -65
- data/guides/source/configuring.md +404 -70
- data/guides/source/contributing_to_ruby_on_rails.md +270 -130
- data/guides/source/credits.html.erb +7 -3
- data/guides/source/debugging_rails_applications.md +471 -284
- data/guides/source/development_dependencies_install.md +115 -21
- data/guides/source/documents.yaml +31 -9
- data/guides/source/engines.md +737 -291
- data/guides/source/form_helpers.md +137 -89
- data/guides/source/generators.md +60 -28
- data/guides/source/getting_started.md +1007 -596
- data/guides/source/i18n.md +178 -96
- data/guides/source/index.html.erb +2 -1
- data/guides/source/initialization.md +248 -104
- data/guides/source/kindle/toc.html.erb +1 -1
- data/guides/source/layout.html.erb +14 -22
- data/guides/source/layouts_and_rendering.md +78 -46
- data/guides/source/maintenance_policy.md +78 -0
- data/guides/source/nested_model_forms.md +10 -7
- data/guides/source/plugins.md +66 -57
- data/guides/source/rails_application_templates.md +49 -12
- data/guides/source/rails_on_rack.md +50 -60
- data/guides/source/routing.md +190 -139
- data/guides/source/ruby_on_rails_guides_guidelines.md +12 -13
- data/guides/source/security.md +134 -83
- data/guides/source/testing.md +322 -200
- data/guides/source/upgrading_ruby_on_rails.md +834 -37
- data/guides/source/working_with_javascript_in_rails.md +36 -26
- data/guides/w3c_validator.rb +2 -0
- metadata +93 -116
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png +0 -0
- data/guides/assets/images/getting_started/new_post.png +0 -0
- data/guides/assets/images/getting_started/post_with_comments.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_posts.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_posts_new.png +0 -0
- data/guides/assets/images/getting_started/undefined_method_post_path.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_posts.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_posts.png +0 -0
- data/guides/assets/images/jaimeiniesta.jpg +0 -0
- data/guides/code/getting_started/Gemfile +0 -43
- data/guides/code/getting_started/Gemfile.lock +0 -150
- data/guides/code/getting_started/README.rdoc +0 -28
- data/guides/code/getting_started/Rakefile +0 -6
- data/guides/code/getting_started/app/assets/javascripts/application.js +0 -16
- data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
- data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
- data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
- data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
- data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -17
- data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -47
- data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
- data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
- data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
- data/guides/code/getting_started/app/models/comment.rb +0 -3
- data/guides/code/getting_started/app/models/post.rb +0 -7
- data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
- data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
- data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
- data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
- data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
- data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
- data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
- data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -3
- data/guides/code/getting_started/bin/bundle +0 -4
- data/guides/code/getting_started/bin/rails +0 -4
- data/guides/code/getting_started/bin/rake +0 -4
- data/guides/code/getting_started/config.ru +0 -4
- data/guides/code/getting_started/config/application.rb +0 -18
- data/guides/code/getting_started/config/boot.rb +0 -4
- data/guides/code/getting_started/config/database.yml +0 -25
- data/guides/code/getting_started/config/environment.rb +0 -5
- data/guides/code/getting_started/config/environments/development.rb +0 -30
- data/guides/code/getting_started/config/environments/production.rb +0 -80
- data/guides/code/getting_started/config/environments/test.rb +0 -36
- data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
- data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
- data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
- data/guides/code/getting_started/config/initializers/locale.rb +0 -9
- data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
- data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
- data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
- data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
- data/guides/code/getting_started/config/locales/en.yml +0 -23
- data/guides/code/getting_started/config/routes.rb +0 -7
- data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
- data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
- data/guides/code/getting_started/db/schema.rb +0 -33
- data/guides/code/getting_started/db/seeds.rb +0 -7
- data/guides/code/getting_started/public/404.html +0 -58
- data/guides/code/getting_started/public/422.html +0 -58
- data/guides/code/getting_started/public/500.html +0 -57
- data/guides/code/getting_started/public/favicon.ico +0 -0
- data/guides/code/getting_started/public/robots.txt +0 -5
- data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
- data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
- data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
- data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
- data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
- data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
- data/guides/code/getting_started/test/models/comment_test.rb +0 -7
- data/guides/code/getting_started/test/models/post_test.rb +0 -7
- data/guides/code/getting_started/test/test_helper.rb +0 -15
- data/guides/source/kindle/KINDLE.md +0 -26
@@ -0,0 +1,433 @@
|
|
1
|
+
Active Record and PostgreSQL
|
2
|
+
============================
|
3
|
+
|
4
|
+
This guide covers PostgreSQL specific usage of Active Record.
|
5
|
+
|
6
|
+
After reading this guide, you will know:
|
7
|
+
|
8
|
+
* How to use PostgreSQL's datatypes.
|
9
|
+
* How to use UUID primary keys.
|
10
|
+
* How to implement full text search with PostgreSQL.
|
11
|
+
* How to back your Active Record models with database views.
|
12
|
+
|
13
|
+
--------------------------------------------------------------------------------
|
14
|
+
|
15
|
+
In order to use the PostgreSQL adapter you need to have at least version 8.2
|
16
|
+
installed. Older versions are not supported.
|
17
|
+
|
18
|
+
To get started with PostgreSQL have a look at the
|
19
|
+
[configuring Rails guide](configuring.html#configuring-a-postgresql-database).
|
20
|
+
It describes how to properly setup Active Record for PostgreSQL.
|
21
|
+
|
22
|
+
Datatypes
|
23
|
+
---------
|
24
|
+
|
25
|
+
PostgreSQL offers a number of specific datatypes. Following is a list of types,
|
26
|
+
that are supported by the PostgreSQL adapter.
|
27
|
+
|
28
|
+
### Bytea
|
29
|
+
|
30
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-binary.html)
|
31
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-binarystring.html)
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# db/migrate/20140207133952_create_documents.rb
|
35
|
+
create_table :documents do |t|
|
36
|
+
t.binary 'payload'
|
37
|
+
end
|
38
|
+
|
39
|
+
# app/models/document.rb
|
40
|
+
class Document < ActiveRecord::Base
|
41
|
+
end
|
42
|
+
|
43
|
+
# Usage
|
44
|
+
data = File.read(Rails.root + "tmp/output.pdf")
|
45
|
+
Document.create payload: data
|
46
|
+
```
|
47
|
+
|
48
|
+
### Array
|
49
|
+
|
50
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/arrays.html)
|
51
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-array.html)
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# db/migrate/20140207133952_create_books.rb
|
55
|
+
create_table :books do |t|
|
56
|
+
t.string 'title'
|
57
|
+
t.string 'tags', array: true
|
58
|
+
t.integer 'ratings', array: true
|
59
|
+
end
|
60
|
+
add_index :books, :tags, using: 'gin'
|
61
|
+
add_index :books, :ratings, using: 'gin'
|
62
|
+
|
63
|
+
# app/models/book.rb
|
64
|
+
class Book < ActiveRecord::Base
|
65
|
+
end
|
66
|
+
|
67
|
+
# Usage
|
68
|
+
Book.create title: "Brave New World",
|
69
|
+
tags: ["fantasy", "fiction"],
|
70
|
+
ratings: [4, 5]
|
71
|
+
|
72
|
+
## Books for a single tag
|
73
|
+
Book.where("'fantasy' = ANY (tags)")
|
74
|
+
|
75
|
+
## Books for multiple tags
|
76
|
+
Book.where("tags @> ARRAY[?]::varchar[]", ["fantasy", "fiction"])
|
77
|
+
|
78
|
+
## Books with 3 or more ratings
|
79
|
+
Book.where("array_length(ratings, 1) >= 3")
|
80
|
+
```
|
81
|
+
|
82
|
+
### Hstore
|
83
|
+
|
84
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/hstore.html)
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# db/migrate/20131009135255_create_profiles.rb
|
88
|
+
ActiveRecord::Schema.define do
|
89
|
+
create_table :profiles do |t|
|
90
|
+
t.hstore 'settings'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# app/models/profile.rb
|
95
|
+
class Profile < ActiveRecord::Base
|
96
|
+
end
|
97
|
+
|
98
|
+
# Usage
|
99
|
+
Profile.create(settings: { "color" => "blue", "resolution" => "800x600" })
|
100
|
+
|
101
|
+
profile = Profile.first
|
102
|
+
profile.settings # => {"color"=>"blue", "resolution"=>"800x600"}
|
103
|
+
|
104
|
+
profile.settings = {"color" => "yellow", "resolution" => "1280x1024"}
|
105
|
+
profile.save!
|
106
|
+
```
|
107
|
+
|
108
|
+
### JSON
|
109
|
+
|
110
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-json.html)
|
111
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-json.html)
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# db/migrate/20131220144913_create_events.rb
|
115
|
+
create_table :events do |t|
|
116
|
+
t.json 'payload'
|
117
|
+
end
|
118
|
+
|
119
|
+
# app/models/event.rb
|
120
|
+
class Event < ActiveRecord::Base
|
121
|
+
end
|
122
|
+
|
123
|
+
# Usage
|
124
|
+
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
|
125
|
+
|
126
|
+
event = Event.first
|
127
|
+
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}
|
128
|
+
|
129
|
+
## Query based on JSON document
|
130
|
+
# The -> operator returns the original JSON type (which might be an object), whereas ->> returns text
|
131
|
+
Event.where("payload->>'kind' = ?", "user_renamed")
|
132
|
+
```
|
133
|
+
|
134
|
+
### Range Types
|
135
|
+
|
136
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/rangetypes.html)
|
137
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-range.html)
|
138
|
+
|
139
|
+
This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.1.1/Range.html) objects.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# db/migrate/20130923065404_create_events.rb
|
143
|
+
create_table :events do |t|
|
144
|
+
t.daterange 'duration'
|
145
|
+
end
|
146
|
+
|
147
|
+
# app/models/event.rb
|
148
|
+
class Event < ActiveRecord::Base
|
149
|
+
end
|
150
|
+
|
151
|
+
# Usage
|
152
|
+
Event.create(duration: Date.new(2014, 2, 11)..Date.new(2014, 2, 12))
|
153
|
+
|
154
|
+
event = Event.first
|
155
|
+
event.duration # => Tue, 11 Feb 2014...Thu, 13 Feb 2014
|
156
|
+
|
157
|
+
## All Events on a given date
|
158
|
+
Event.where("duration @> ?::date", Date.new(2014, 2, 12))
|
159
|
+
|
160
|
+
## Working with range bounds
|
161
|
+
event = Event.
|
162
|
+
select("lower(duration) AS starts_at").
|
163
|
+
select("upper(duration) AS ends_at").first
|
164
|
+
|
165
|
+
event.starts_at # => Tue, 11 Feb 2014
|
166
|
+
event.ends_at # => Thu, 13 Feb 2014
|
167
|
+
```
|
168
|
+
|
169
|
+
### Composite Types
|
170
|
+
|
171
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/rowtypes.html)
|
172
|
+
|
173
|
+
Currently there is no special support for composite types. They are mapped to
|
174
|
+
normal text columns:
|
175
|
+
|
176
|
+
```sql
|
177
|
+
CREATE TYPE full_address AS
|
178
|
+
(
|
179
|
+
city VARCHAR(90),
|
180
|
+
street VARCHAR(90)
|
181
|
+
);
|
182
|
+
```
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# db/migrate/20140207133952_create_contacts.rb
|
186
|
+
execute <<-SQL
|
187
|
+
CREATE TYPE full_address AS
|
188
|
+
(
|
189
|
+
city VARCHAR(90),
|
190
|
+
street VARCHAR(90)
|
191
|
+
);
|
192
|
+
SQL
|
193
|
+
create_table :contacts do |t|
|
194
|
+
t.column :address, :full_address
|
195
|
+
end
|
196
|
+
|
197
|
+
# app/models/contact.rb
|
198
|
+
class Contact < ActiveRecord::Base
|
199
|
+
end
|
200
|
+
|
201
|
+
# Usage
|
202
|
+
Contact.create address: "(Paris,Champs-Élysées)"
|
203
|
+
contact = Contact.first
|
204
|
+
contact.address # => "(Paris,Champs-Élysées)"
|
205
|
+
contact.address = "(Paris,Rue Basse)"
|
206
|
+
contact.save!
|
207
|
+
```
|
208
|
+
|
209
|
+
### Enumerated Types
|
210
|
+
|
211
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-enum.html)
|
212
|
+
|
213
|
+
Currently there is no special support for enumerated types. They are mapped as
|
214
|
+
normal text columns:
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
# db/migrate/20131220144913_create_articles.rb
|
218
|
+
execute <<-SQL
|
219
|
+
CREATE TYPE article_status AS ENUM ('draft', 'published');
|
220
|
+
SQL
|
221
|
+
create_table :articles do |t|
|
222
|
+
t.column :status, :article_status
|
223
|
+
end
|
224
|
+
|
225
|
+
# app/models/article.rb
|
226
|
+
class Article < ActiveRecord::Base
|
227
|
+
end
|
228
|
+
|
229
|
+
# Usage
|
230
|
+
Article.create status: "draft"
|
231
|
+
article = Article.first
|
232
|
+
article.status # => "draft"
|
233
|
+
|
234
|
+
article.status = "published"
|
235
|
+
article.save!
|
236
|
+
```
|
237
|
+
|
238
|
+
### UUID
|
239
|
+
|
240
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-uuid.html)
|
241
|
+
* [generator functions](http://www.postgresql.org/docs/9.3/static/uuid-ossp.html)
|
242
|
+
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# db/migrate/20131220144913_create_revisions.rb
|
246
|
+
create_table :revisions do |t|
|
247
|
+
t.column :identifier, :uuid
|
248
|
+
end
|
249
|
+
|
250
|
+
# app/models/revision.rb
|
251
|
+
class Revision < ActiveRecord::Base
|
252
|
+
end
|
253
|
+
|
254
|
+
# Usage
|
255
|
+
Revision.create identifier: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"
|
256
|
+
|
257
|
+
revision = Revision.first
|
258
|
+
revision.identifier # => "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
|
259
|
+
```
|
260
|
+
|
261
|
+
### Bit String Types
|
262
|
+
|
263
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-bit.html)
|
264
|
+
* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-bitstring.html)
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
# db/migrate/20131220144913_create_users.rb
|
268
|
+
create_table :users, force: true do |t|
|
269
|
+
t.column :settings, "bit(8)"
|
270
|
+
end
|
271
|
+
|
272
|
+
# app/models/device.rb
|
273
|
+
class User < ActiveRecord::Base
|
274
|
+
end
|
275
|
+
|
276
|
+
# Usage
|
277
|
+
User.create settings: "01010011"
|
278
|
+
user = User.first
|
279
|
+
user.settings # => "01010011"
|
280
|
+
user.settings = "0xAF"
|
281
|
+
user.settings # => 10101111
|
282
|
+
user.save!
|
283
|
+
```
|
284
|
+
|
285
|
+
### Network Address Types
|
286
|
+
|
287
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-net-types.html)
|
288
|
+
|
289
|
+
The types `inet` and `cidr` are mapped to Ruby
|
290
|
+
[`IPAddr`](http://www.ruby-doc.org/stdlib-2.1.1/libdoc/ipaddr/rdoc/IPAddr.html)
|
291
|
+
objects. The `macaddr` type is mapped to normal text.
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
# db/migrate/20140508144913_create_devices.rb
|
295
|
+
create_table(:devices, force: true) do |t|
|
296
|
+
t.inet 'ip'
|
297
|
+
t.cidr 'network'
|
298
|
+
t.macaddr 'address'
|
299
|
+
end
|
300
|
+
|
301
|
+
# app/models/device.rb
|
302
|
+
class Device < ActiveRecord::Base
|
303
|
+
end
|
304
|
+
|
305
|
+
# Usage
|
306
|
+
macbook = Device.create(ip: "192.168.1.12",
|
307
|
+
network: "192.168.2.0/24",
|
308
|
+
address: "32:01:16:6d:05:ef")
|
309
|
+
|
310
|
+
macbook.ip
|
311
|
+
# => #<IPAddr: IPv4:192.168.1.12/255.255.255.255>
|
312
|
+
|
313
|
+
macbook.network
|
314
|
+
# => #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
315
|
+
|
316
|
+
macbook.address
|
317
|
+
# => "32:01:16:6d:05:ef"
|
318
|
+
```
|
319
|
+
|
320
|
+
### Geometric Types
|
321
|
+
|
322
|
+
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-geometric.html)
|
323
|
+
|
324
|
+
All geometric types, with the exception of `points` are mapped to normal text.
|
325
|
+
A point is casted to an array containing `x` and `y` coordinates.
|
326
|
+
|
327
|
+
|
328
|
+
UUID Primary Keys
|
329
|
+
-----------------
|
330
|
+
|
331
|
+
NOTE: you need to enable the `uuid-ossp` extension to generate UUIDs.
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
# db/migrate/20131220144913_create_devices.rb
|
335
|
+
enable_extension 'uuid-ossp' unless extension_enabled?('uuid-ossp')
|
336
|
+
create_table :devices, id: :uuid, default: 'uuid_generate_v4()' do |t|
|
337
|
+
t.string :kind
|
338
|
+
end
|
339
|
+
|
340
|
+
# app/models/device.rb
|
341
|
+
class Device < ActiveRecord::Base
|
342
|
+
end
|
343
|
+
|
344
|
+
# Usage
|
345
|
+
device = Device.create
|
346
|
+
device.id # => "814865cd-5a1d-4771-9306-4268f188fe9e"
|
347
|
+
```
|
348
|
+
|
349
|
+
Full Text Search
|
350
|
+
----------------
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
# db/migrate/20131220144913_create_documents.rb
|
354
|
+
create_table :documents do |t|
|
355
|
+
t.string 'title'
|
356
|
+
t.string 'body'
|
357
|
+
end
|
358
|
+
|
359
|
+
execute "CREATE INDEX documents_idx ON documents USING gin(to_tsvector('english', title || ' ' || body));"
|
360
|
+
|
361
|
+
# app/models/document.rb
|
362
|
+
class Document < ActiveRecord::Base
|
363
|
+
end
|
364
|
+
|
365
|
+
# Usage
|
366
|
+
Document.create(title: "Cats and Dogs", body: "are nice!")
|
367
|
+
|
368
|
+
## all documents matching 'cat & dog'
|
369
|
+
Document.where("to_tsvector('english', title || ' ' || body) @@ to_tsquery(?)",
|
370
|
+
"cat & dog")
|
371
|
+
```
|
372
|
+
|
373
|
+
Database Views
|
374
|
+
--------------
|
375
|
+
|
376
|
+
* [view creation](http://www.postgresql.org/docs/9.3/static/sql-createview.html)
|
377
|
+
|
378
|
+
Imagine you need to work with a legacy database containing the following table:
|
379
|
+
|
380
|
+
```
|
381
|
+
rails_pg_guide=# \d "TBL_ART"
|
382
|
+
Table "public.TBL_ART"
|
383
|
+
Column | Type | Modifiers
|
384
|
+
------------+-----------------------------+------------------------------------------------------------
|
385
|
+
INT_ID | integer | not null default nextval('"TBL_ART_INT_ID_seq"'::regclass)
|
386
|
+
STR_TITLE | character varying |
|
387
|
+
STR_STAT | character varying | default 'draft'::character varying
|
388
|
+
DT_PUBL_AT | timestamp without time zone |
|
389
|
+
BL_ARCH | boolean | default false
|
390
|
+
Indexes:
|
391
|
+
"TBL_ART_pkey" PRIMARY KEY, btree ("INT_ID")
|
392
|
+
```
|
393
|
+
|
394
|
+
This table does not follow the Rails conventions at all.
|
395
|
+
Because simple PostgreSQL views are updateable by default,
|
396
|
+
we can wrap it as follows:
|
397
|
+
|
398
|
+
```ruby
|
399
|
+
# db/migrate/20131220144913_create_articles_view.rb
|
400
|
+
execute <<-SQL
|
401
|
+
CREATE VIEW articles AS
|
402
|
+
SELECT "INT_ID" AS id,
|
403
|
+
"STR_TITLE" AS title,
|
404
|
+
"STR_STAT" AS status,
|
405
|
+
"DT_PUBL_AT" AS published_at,
|
406
|
+
"BL_ARCH" AS archived
|
407
|
+
FROM "TBL_ART"
|
408
|
+
WHERE "BL_ARCH" = 'f'
|
409
|
+
SQL
|
410
|
+
|
411
|
+
# app/models/article.rb
|
412
|
+
class Article < ActiveRecord::Base
|
413
|
+
self.primary_key = "id"
|
414
|
+
def archive!
|
415
|
+
update_attribute :archived, true
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Usage
|
420
|
+
first = Article.create! title: "Winter is coming",
|
421
|
+
status: "published",
|
422
|
+
published_at: 1.year.ago
|
423
|
+
second = Article.create! title: "Brace yourself",
|
424
|
+
status: "draft",
|
425
|
+
published_at: 1.month.ago
|
426
|
+
|
427
|
+
Article.count # => 1
|
428
|
+
first.archive!
|
429
|
+
Article.count # => 2
|
430
|
+
```
|
431
|
+
|
432
|
+
NOTE: This application only cares about non-archived `Articles`. A view also
|
433
|
+
allows for conditions so we can exclude the archived `Articles` directly.
|
@@ -58,6 +58,7 @@ The methods are:
|
|
58
58
|
|
59
59
|
* `bind`
|
60
60
|
* `create_with`
|
61
|
+
* `distinct`
|
61
62
|
* `eager_load`
|
62
63
|
* `extending`
|
63
64
|
* `from`
|
@@ -76,7 +77,6 @@ The methods are:
|
|
76
77
|
* `reorder`
|
77
78
|
* `reverse_order`
|
78
79
|
* `select`
|
79
|
-
* `distinct`
|
80
80
|
* `uniq`
|
81
81
|
* `where`
|
82
82
|
|
@@ -91,11 +91,11 @@ The primary operation of `Model.find(options)` can be summarized as:
|
|
91
91
|
|
92
92
|
### Retrieving a Single Object
|
93
93
|
|
94
|
-
Active Record provides
|
94
|
+
Active Record provides several different ways of retrieving a single object.
|
95
95
|
|
96
|
-
####
|
96
|
+
#### `find`
|
97
97
|
|
98
|
-
Using `
|
98
|
+
Using the `find` method, you can retrieve the object corresponding to the specified _primary key_ that matches any supplied options. For example:
|
99
99
|
|
100
100
|
```ruby
|
101
101
|
# Find the client with primary key (id) 10.
|
@@ -109,119 +109,103 @@ The SQL equivalent of the above is:
|
|
109
109
|
SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1
|
110
110
|
```
|
111
111
|
|
112
|
-
`
|
113
|
-
|
114
|
-
#### `take`
|
112
|
+
The `find` method will raise an `ActiveRecord::RecordNotFound` exception if no matching record is found.
|
115
113
|
|
116
|
-
|
114
|
+
You can also use this method to query for multiple objects. Call the `find` method and pass in an array of primary keys. The return will be an array containing all of the matching records for the supplied _primary keys_. For example:
|
117
115
|
|
118
116
|
```ruby
|
119
|
-
|
120
|
-
|
117
|
+
# Find the clients with primary keys 1 and 10.
|
118
|
+
client = Client.find([1, 10]) # Or even Client.find(1, 10)
|
119
|
+
# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
|
121
120
|
```
|
122
121
|
|
123
122
|
The SQL equivalent of the above is:
|
124
123
|
|
125
124
|
```sql
|
126
|
-
SELECT * FROM clients
|
125
|
+
SELECT * FROM clients WHERE (clients.id IN (1,10))
|
127
126
|
```
|
128
127
|
|
129
|
-
`
|
128
|
+
WARNING: The `find` method will raise an `ActiveRecord::RecordNotFound` exception unless a matching record is found for **all** of the supplied primary keys.
|
130
129
|
|
131
|
-
|
132
|
-
|
133
|
-
#### `first`
|
130
|
+
#### `take`
|
134
131
|
|
135
|
-
`
|
132
|
+
The `take` method retrieves a record without any implicit ordering. For example:
|
136
133
|
|
137
134
|
```ruby
|
138
|
-
client = Client.
|
135
|
+
client = Client.take
|
139
136
|
# => #<Client id: 1, first_name: "Lifo">
|
140
137
|
```
|
141
138
|
|
142
139
|
The SQL equivalent of the above is:
|
143
140
|
|
144
141
|
```sql
|
145
|
-
SELECT * FROM clients
|
142
|
+
SELECT * FROM clients LIMIT 1
|
146
143
|
```
|
147
144
|
|
148
|
-
`
|
145
|
+
The `take` method returns `nil` if no record is found and no exception will be raised.
|
149
146
|
|
150
|
-
|
151
|
-
|
152
|
-
`Model.last` finds the last record ordered by the primary key. For example:
|
147
|
+
You can pass in a numerical argument to the `take` method to return up to that number of results. For example
|
153
148
|
|
154
149
|
```ruby
|
155
|
-
client = Client.
|
156
|
-
# =>
|
150
|
+
client = Client.take(2)
|
151
|
+
# => [
|
152
|
+
#<Client id: 1, first_name: "Lifo">,
|
153
|
+
#<Client id: 220, first_name: "Sara">
|
154
|
+
]
|
157
155
|
```
|
158
156
|
|
159
157
|
The SQL equivalent of the above is:
|
160
158
|
|
161
159
|
```sql
|
162
|
-
SELECT * FROM clients
|
163
|
-
```
|
164
|
-
|
165
|
-
`Model.last` returns `nil` if no matching record is found and no exception will be raised.
|
166
|
-
|
167
|
-
#### `find_by`
|
168
|
-
|
169
|
-
`Model.find_by` finds the first record matching some conditions. For example:
|
170
|
-
|
171
|
-
```ruby
|
172
|
-
Client.find_by first_name: 'Lifo'
|
173
|
-
# => #<Client id: 1, first_name: "Lifo">
|
174
|
-
|
175
|
-
Client.find_by first_name: 'Jon'
|
176
|
-
# => nil
|
160
|
+
SELECT * FROM clients LIMIT 2
|
177
161
|
```
|
178
162
|
|
179
|
-
|
163
|
+
The `take!` method behaves exactly like `take`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
180
164
|
|
181
|
-
|
182
|
-
Client.where(first_name: 'Lifo').take
|
183
|
-
```
|
165
|
+
TIP: The retrieved record may vary depending on the database engine.
|
184
166
|
|
185
|
-
#### `
|
167
|
+
#### `first`
|
186
168
|
|
187
|
-
`
|
169
|
+
The `first` method finds the first record ordered by the primary key. For example:
|
188
170
|
|
189
171
|
```ruby
|
190
|
-
client = Client.
|
172
|
+
client = Client.first
|
191
173
|
# => #<Client id: 1, first_name: "Lifo">
|
192
174
|
```
|
193
175
|
|
194
176
|
The SQL equivalent of the above is:
|
195
177
|
|
196
178
|
```sql
|
197
|
-
SELECT * FROM clients LIMIT 1
|
179
|
+
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 1
|
198
180
|
```
|
199
181
|
|
200
|
-
`
|
182
|
+
The `first` method returns `nil` if no matching record is found and no exception will be raised.
|
201
183
|
|
202
|
-
|
203
|
-
|
204
|
-
`Model.first!` finds the first record ordered by the primary key. For example:
|
184
|
+
You can pass in a numerical argument to the `first` method to return up to that number of results. For example
|
205
185
|
|
206
186
|
```ruby
|
207
|
-
client = Client.first
|
208
|
-
# =>
|
187
|
+
client = Client.first(3)
|
188
|
+
# => [
|
189
|
+
#<Client id: 1, first_name: "Lifo">,
|
190
|
+
#<Client id: 2, first_name: "Fifo">,
|
191
|
+
#<Client id: 3, first_name: "Filo">
|
192
|
+
]
|
209
193
|
```
|
210
194
|
|
211
195
|
The SQL equivalent of the above is:
|
212
196
|
|
213
197
|
```sql
|
214
|
-
SELECT * FROM clients ORDER BY clients.id ASC LIMIT
|
198
|
+
SELECT * FROM clients ORDER BY clients.id ASC LIMIT 3
|
215
199
|
```
|
216
200
|
|
217
|
-
`
|
201
|
+
The `first!` method behaves exactly like `first`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
218
202
|
|
219
|
-
#### `last
|
203
|
+
#### `last`
|
220
204
|
|
221
|
-
`
|
205
|
+
The `last` method finds the last record ordered by the primary key. For example:
|
222
206
|
|
223
207
|
```ruby
|
224
|
-
client = Client.last
|
208
|
+
client = Client.last
|
225
209
|
# => #<Client id: 221, first_name: "Russel">
|
226
210
|
```
|
227
211
|
|
@@ -231,92 +215,56 @@ The SQL equivalent of the above is:
|
|
231
215
|
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
|
232
216
|
```
|
233
217
|
|
234
|
-
`
|
235
|
-
|
236
|
-
#### `find_by!`
|
237
|
-
|
238
|
-
`Model.find_by!` finds the first record matching some conditions. It raises `ActiveRecord::RecordNotFound` if no matching record is found. For example:
|
239
|
-
|
240
|
-
```ruby
|
241
|
-
Client.find_by! first_name: 'Lifo'
|
242
|
-
# => #<Client id: 1, first_name: "Lifo">
|
243
|
-
|
244
|
-
Client.find_by! first_name: 'Jon'
|
245
|
-
# => ActiveRecord::RecordNotFound
|
246
|
-
```
|
247
|
-
|
248
|
-
It is equivalent to writing:
|
249
|
-
|
250
|
-
```ruby
|
251
|
-
Client.where(first_name: 'Lifo').take!
|
252
|
-
```
|
253
|
-
|
254
|
-
### Retrieving Multiple Objects
|
255
|
-
|
256
|
-
#### Using Multiple Primary Keys
|
218
|
+
The `last` method returns `nil` if no matching record is found and no exception will be raised.
|
257
219
|
|
258
|
-
|
220
|
+
You can pass in a numerical argument to the `last` method to return up to that number of results. For example
|
259
221
|
|
260
222
|
```ruby
|
261
|
-
|
262
|
-
|
263
|
-
|
223
|
+
client = Client.last(3)
|
224
|
+
# => [
|
225
|
+
#<Client id: 219, first_name: "James">,
|
226
|
+
#<Client id: 220, first_name: "Sara">,
|
227
|
+
#<Client id: 221, first_name: "Russel">
|
228
|
+
]
|
264
229
|
```
|
265
230
|
|
266
231
|
The SQL equivalent of the above is:
|
267
232
|
|
268
233
|
```sql
|
269
|
-
SELECT * FROM clients
|
234
|
+
SELECT * FROM clients ORDER BY clients.id DESC LIMIT 3
|
270
235
|
```
|
271
236
|
|
272
|
-
|
237
|
+
The `last!` method behaves exactly like `last`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found.
|
273
238
|
|
274
|
-
####
|
239
|
+
#### `find_by`
|
275
240
|
|
276
|
-
`
|
241
|
+
The `find_by` method finds the first record matching some conditions. For example:
|
277
242
|
|
278
243
|
```ruby
|
279
|
-
Client.
|
280
|
-
# =>
|
281
|
-
#<Client id: 2, first_name: "Raf">]
|
282
|
-
```
|
283
|
-
|
284
|
-
The SQL equivalent of the above is:
|
244
|
+
Client.find_by first_name: 'Lifo'
|
245
|
+
# => #<Client id: 1, first_name: "Lifo">
|
285
246
|
|
286
|
-
|
287
|
-
|
247
|
+
Client.find_by first_name: 'Jon'
|
248
|
+
# => nil
|
288
249
|
```
|
289
250
|
|
290
|
-
|
291
|
-
|
292
|
-
`Model.first(limit)` finds the first number of records specified by `limit` ordered by primary key:
|
251
|
+
It is equivalent to writing:
|
293
252
|
|
294
253
|
```ruby
|
295
|
-
Client.
|
296
|
-
# => [#<Client id: 1, first_name: "Lifo">,
|
297
|
-
#<Client id: 2, first_name: "Raf">]
|
298
|
-
```
|
299
|
-
|
300
|
-
The SQL equivalent of the above is:
|
301
|
-
|
302
|
-
```sql
|
303
|
-
SELECT * FROM clients ORDER BY id ASC LIMIT 2
|
254
|
+
Client.where(first_name: 'Lifo').take
|
304
255
|
```
|
305
256
|
|
306
|
-
|
307
|
-
|
308
|
-
`Model.last(limit)` finds the number of records specified by `limit` ordered by primary key in descending order:
|
257
|
+
The `find_by!` method behaves exactly like `find_by`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. For example:
|
309
258
|
|
310
259
|
```ruby
|
311
|
-
Client.
|
312
|
-
# =>
|
313
|
-
#<Client id: 9, first_name: "John">]
|
260
|
+
Client.find_by! first_name: 'does not exist'
|
261
|
+
# => ActiveRecord::RecordNotFound
|
314
262
|
```
|
315
263
|
|
316
|
-
|
264
|
+
This is equivalent to writing:
|
317
265
|
|
318
|
-
```
|
319
|
-
|
266
|
+
```ruby
|
267
|
+
Client.where(first_name: 'does not exist').take!
|
320
268
|
```
|
321
269
|
|
322
270
|
### Retrieving Multiple Objects in Batches
|
@@ -328,7 +276,7 @@ This may appear straightforward:
|
|
328
276
|
```ruby
|
329
277
|
# This is very inefficient when the users table has thousands of rows.
|
330
278
|
User.all.each do |user|
|
331
|
-
|
279
|
+
NewsMailer.weekly(user).deliver_now
|
332
280
|
end
|
333
281
|
```
|
334
282
|
|
@@ -344,7 +292,15 @@ The `find_each` method retrieves a batch of records and then yields _each_ recor
|
|
344
292
|
|
345
293
|
```ruby
|
346
294
|
User.find_each do |user|
|
347
|
-
|
295
|
+
NewsMailer.weekly(user).deliver_now
|
296
|
+
end
|
297
|
+
```
|
298
|
+
|
299
|
+
To add conditions to a `find_each` operation you can chain other Active Record methods such as `where`:
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
User.where(weekly_subscriber: true).find_each do |user|
|
303
|
+
NewsMailer.weekly(user).deliver_now
|
348
304
|
end
|
349
305
|
```
|
350
306
|
|
@@ -360,7 +316,7 @@ The `:batch_size` option allows you to specify the number of records to be retri
|
|
360
316
|
|
361
317
|
```ruby
|
362
318
|
User.find_each(batch_size: 5000) do |user|
|
363
|
-
|
319
|
+
NewsMailer.weekly(user).deliver_now
|
364
320
|
end
|
365
321
|
```
|
366
322
|
|
@@ -372,28 +328,24 @@ For example, to send newsletters only to users with the primary key starting fro
|
|
372
328
|
|
373
329
|
```ruby
|
374
330
|
User.find_each(start: 2000, batch_size: 5000) do |user|
|
375
|
-
|
331
|
+
NewsMailer.weekly(user).deliver_now
|
376
332
|
end
|
377
333
|
```
|
378
334
|
|
379
|
-
Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate `:start` option on each worker.
|
380
|
-
|
381
335
|
#### `find_in_batches`
|
382
336
|
|
383
337
|
The `find_in_batches` method is similar to `find_each`, since both retrieve batches of records. The difference is that `find_in_batches` yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices:
|
384
338
|
|
385
339
|
```ruby
|
386
340
|
# Give add_invoices an array of 1000 invoices at a time
|
387
|
-
Invoice.find_in_batches
|
341
|
+
Invoice.find_in_batches do |invoices|
|
388
342
|
export.add_invoices(invoices)
|
389
343
|
end
|
390
344
|
```
|
391
345
|
|
392
|
-
NOTE: The `:include` option allows you to name associations that should be loaded alongside with the models.
|
393
|
-
|
394
346
|
##### Options for `find_in_batches`
|
395
347
|
|
396
|
-
The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each
|
348
|
+
The `find_in_batches` method accepts the same `:batch_size` and `:start` options as `find_each`.
|
397
349
|
|
398
350
|
Conditions
|
399
351
|
----------
|
@@ -436,7 +388,7 @@ to this code:
|
|
436
388
|
Client.where("orders_count = #{params[:orders]}")
|
437
389
|
```
|
438
390
|
|
439
|
-
because of argument 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
|
391
|
+
because of argument 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 they can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
|
440
392
|
|
441
393
|
TIP: For more information on the dangers of SQL injection, see the [Ruby on Rails Security Guide](security.html#sql-injection).
|
442
394
|
|
@@ -472,8 +424,8 @@ Client.where('locked' => true)
|
|
472
424
|
In the case of a belongs_to relationship, an association key can be used to specify the model if an Active Record object is used as the value. This method works with polymorphic relationships as well.
|
473
425
|
|
474
426
|
```ruby
|
475
|
-
|
476
|
-
Author.joins(:
|
427
|
+
Article.where(author: author)
|
428
|
+
Author.joins(:articles).where(articles: { author: author })
|
477
429
|
```
|
478
430
|
|
479
431
|
NOTE: The values cannot be symbols. For example, you cannot do `Client.where(status: :active)`.
|
@@ -511,7 +463,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
|
|
511
463
|
`NOT` SQL queries can be built by `where.not`.
|
512
464
|
|
513
465
|
```ruby
|
514
|
-
|
466
|
+
Article.where.not(author: author)
|
515
467
|
```
|
516
468
|
|
517
469
|
In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions.
|
@@ -524,12 +476,18 @@ To retrieve records from the database in a specific order, you can use the `orde
|
|
524
476
|
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:
|
525
477
|
|
526
478
|
```ruby
|
479
|
+
Client.order(:created_at)
|
480
|
+
# OR
|
527
481
|
Client.order("created_at")
|
528
482
|
```
|
529
483
|
|
530
484
|
You could specify `ASC` or `DESC` as well:
|
531
485
|
|
532
486
|
```ruby
|
487
|
+
Client.order(created_at: :desc)
|
488
|
+
# OR
|
489
|
+
Client.order(created_at: :asc)
|
490
|
+
# OR
|
533
491
|
Client.order("created_at DESC")
|
534
492
|
# OR
|
535
493
|
Client.order("created_at ASC")
|
@@ -538,16 +496,20 @@ Client.order("created_at ASC")
|
|
538
496
|
Or ordering by multiple fields:
|
539
497
|
|
540
498
|
```ruby
|
499
|
+
Client.order(orders_count: :asc, created_at: :desc)
|
500
|
+
# OR
|
501
|
+
Client.order(:orders_count, created_at: :desc)
|
502
|
+
# OR
|
541
503
|
Client.order("orders_count ASC, created_at DESC")
|
542
504
|
# OR
|
543
505
|
Client.order("orders_count ASC", "created_at DESC")
|
544
506
|
```
|
545
507
|
|
546
|
-
If you want to call `order` multiple times e.g. in different context, new order will
|
508
|
+
If you want to call `order` multiple times e.g. in different context, new order will append previous one
|
547
509
|
|
548
510
|
```ruby
|
549
511
|
Client.order("orders_count ASC").order("created_at DESC")
|
550
|
-
# SELECT * FROM clients ORDER BY
|
512
|
+
# SELECT * FROM clients ORDER BY orders_count ASC, created_at DESC
|
551
513
|
```
|
552
514
|
|
553
515
|
Selecting Specific Fields
|
@@ -649,6 +611,23 @@ FROM orders
|
|
649
611
|
GROUP BY date(created_at)
|
650
612
|
```
|
651
613
|
|
614
|
+
### Total of grouped items
|
615
|
+
|
616
|
+
To get the total of grouped items on a single query call `count` after the `group`.
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
Order.group(:status).count
|
620
|
+
# => { 'awaiting_approval' => 7, 'paid' => 12 }
|
621
|
+
```
|
622
|
+
|
623
|
+
The SQL that would be executed would be something like this:
|
624
|
+
|
625
|
+
```sql
|
626
|
+
SELECT COUNT (*) AS count_all, status AS status
|
627
|
+
FROM "orders"
|
628
|
+
GROUP BY status
|
629
|
+
```
|
630
|
+
|
652
631
|
Having
|
653
632
|
------
|
654
633
|
|
@@ -675,39 +654,37 @@ This will return single order objects for each day, but only those that are orde
|
|
675
654
|
Overriding Conditions
|
676
655
|
---------------------
|
677
656
|
|
678
|
-
### `
|
657
|
+
### `unscope`
|
679
658
|
|
680
|
-
You can specify certain conditions to be
|
659
|
+
You can specify certain conditions to be removed using the `unscope` method. For example:
|
681
660
|
|
682
661
|
```ruby
|
683
|
-
|
662
|
+
Article.where('id > 10').limit(20).order('id asc').unscope(:order)
|
684
663
|
```
|
685
664
|
|
686
665
|
The SQL that would be executed:
|
687
666
|
|
688
667
|
```sql
|
689
|
-
SELECT * FROM
|
690
|
-
```
|
691
|
-
|
692
|
-
### `unscope`
|
668
|
+
SELECT * FROM articles WHERE id > 10 LIMIT 20
|
693
669
|
|
694
|
-
|
670
|
+
# Original query without `unscope`
|
671
|
+
SELECT * FROM articles WHERE id > 10 ORDER BY id asc LIMIT 20
|
695
672
|
|
696
|
-
```ruby
|
697
|
-
Post.comments.except(:order)
|
698
673
|
```
|
699
674
|
|
700
|
-
|
675
|
+
You can also unscope specific `where` clauses. For example:
|
701
676
|
|
702
677
|
```ruby
|
703
|
-
|
704
|
-
|
678
|
+
Article.where(id: 10, trashed: false).unscope(where: :id)
|
679
|
+
# SELECT "articles".* FROM "articles" WHERE trashed = 0
|
705
680
|
```
|
706
681
|
|
707
|
-
|
682
|
+
A relation which has used `unscope` will affect any relation it is
|
683
|
+
merged in to:
|
708
684
|
|
709
685
|
```ruby
|
710
|
-
|
686
|
+
Article.order('id asc').merge(Article.unscope(:order))
|
687
|
+
# SELECT "articles".* FROM "articles"
|
711
688
|
```
|
712
689
|
|
713
690
|
### `only`
|
@@ -715,13 +692,17 @@ Post.where(:id => 10).limit(1).unscope(where: :id, :limit).order('id DESC') = Po
|
|
715
692
|
You can also override conditions using the `only` method. For example:
|
716
693
|
|
717
694
|
```ruby
|
718
|
-
|
695
|
+
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
|
719
696
|
```
|
720
697
|
|
721
698
|
The SQL that would be executed:
|
722
699
|
|
723
700
|
```sql
|
724
|
-
SELECT * FROM
|
701
|
+
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC
|
702
|
+
|
703
|
+
# Original query without `only`
|
704
|
+
SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20
|
705
|
+
|
725
706
|
```
|
726
707
|
|
727
708
|
### `reorder`
|
@@ -729,25 +710,25 @@ SELECT * FROM posts WHERE id > 10 ORDER BY id DESC
|
|
729
710
|
The `reorder` method overrides the default scope order. For example:
|
730
711
|
|
731
712
|
```ruby
|
732
|
-
class
|
733
|
-
|
734
|
-
..
|
735
|
-
has_many :comments, order: 'posted_at DESC'
|
713
|
+
class Article < ActiveRecord::Base
|
714
|
+
has_many :comments, -> { order('posted_at DESC') }
|
736
715
|
end
|
737
716
|
|
738
|
-
|
717
|
+
Article.find(10).comments.reorder('name')
|
739
718
|
```
|
740
719
|
|
741
720
|
The SQL that would be executed:
|
742
721
|
|
743
722
|
```sql
|
744
|
-
SELECT * FROM
|
723
|
+
SELECT * FROM articles WHERE id = 10
|
724
|
+
SELECT * FROM comments WHERE article_id = 10 ORDER BY name
|
745
725
|
```
|
746
726
|
|
747
727
|
In case the `reorder` clause is not used, the SQL executed would be:
|
748
728
|
|
749
729
|
```sql
|
750
|
-
SELECT * FROM
|
730
|
+
SELECT * FROM articles WHERE id = 10
|
731
|
+
SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC
|
751
732
|
```
|
752
733
|
|
753
734
|
### `reverse_order`
|
@@ -778,27 +759,53 @@ SELECT * FROM clients WHERE orders_count > 10 ORDER BY clients.id DESC
|
|
778
759
|
|
779
760
|
This method accepts **no** arguments.
|
780
761
|
|
762
|
+
### `rewhere`
|
763
|
+
|
764
|
+
The `rewhere` method overrides an existing, named where condition. For example:
|
765
|
+
|
766
|
+
```ruby
|
767
|
+
Article.where(trashed: true).rewhere(trashed: false)
|
768
|
+
```
|
769
|
+
|
770
|
+
The SQL that would be executed:
|
771
|
+
|
772
|
+
```sql
|
773
|
+
SELECT * FROM articles WHERE `trashed` = 0
|
774
|
+
```
|
775
|
+
|
776
|
+
In case the `rewhere` clause is not used,
|
777
|
+
|
778
|
+
```ruby
|
779
|
+
Article.where(trashed: true).where(trashed: false)
|
780
|
+
```
|
781
|
+
|
782
|
+
the SQL executed would be:
|
783
|
+
|
784
|
+
```sql
|
785
|
+
SELECT * FROM articles WHERE `trashed` = 1 AND `trashed` = 0
|
786
|
+
```
|
787
|
+
|
781
788
|
Null Relation
|
782
789
|
-------------
|
783
790
|
|
784
791
|
The `none` method returns a chainable relation with no records. Any subsequent conditions chained to the returned relation will continue generating empty relations. This is useful in scenarios where you need a chainable response to a method or a scope that could return zero results.
|
785
792
|
|
786
793
|
```ruby
|
787
|
-
|
794
|
+
Article.none # returns an empty Relation and fires no queries.
|
788
795
|
```
|
789
796
|
|
790
797
|
```ruby
|
791
|
-
# The
|
792
|
-
@
|
798
|
+
# The visible_articles method below is expected to return a Relation.
|
799
|
+
@articles = current_user.visible_articles.where(name: params[:name])
|
793
800
|
|
794
|
-
def
|
801
|
+
def visible_articles
|
795
802
|
case role
|
796
803
|
when 'Country Manager'
|
797
|
-
|
804
|
+
Article.where(country: country)
|
798
805
|
when 'Reviewer'
|
799
|
-
|
806
|
+
Article.published
|
800
807
|
when 'Bad User'
|
801
|
-
|
808
|
+
Article.none # => returning [] or nil breaks the caller code in this case
|
802
809
|
end
|
803
810
|
end
|
804
811
|
```
|
@@ -867,7 +874,7 @@ For example:
|
|
867
874
|
Item.transaction do
|
868
875
|
i = Item.lock.first
|
869
876
|
i.name = 'Jones'
|
870
|
-
i.save
|
877
|
+
i.save!
|
871
878
|
end
|
872
879
|
```
|
873
880
|
|
@@ -923,23 +930,23 @@ SELECT clients.* FROM clients LEFT OUTER JOIN addresses ON addresses.client_id =
|
|
923
930
|
|
924
931
|
WARNING: This method only works with `INNER JOIN`.
|
925
932
|
|
926
|
-
Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN`
|
933
|
+
Active Record lets you use the names of the [associations](association_basics.html) defined on the model as a shortcut for specifying `JOIN` clauses for those associations when using the `joins` method.
|
927
934
|
|
928
|
-
For example, consider the following `Category`, `
|
935
|
+
For example, consider the following `Category`, `Article`, `Comment`, `Guest` and `Tag` models:
|
929
936
|
|
930
937
|
```ruby
|
931
938
|
class Category < ActiveRecord::Base
|
932
|
-
has_many :
|
939
|
+
has_many :articles
|
933
940
|
end
|
934
941
|
|
935
|
-
class
|
942
|
+
class Article < ActiveRecord::Base
|
936
943
|
belongs_to :category
|
937
944
|
has_many :comments
|
938
945
|
has_many :tags
|
939
946
|
end
|
940
947
|
|
941
948
|
class Comment < ActiveRecord::Base
|
942
|
-
belongs_to :
|
949
|
+
belongs_to :article
|
943
950
|
has_one :guest
|
944
951
|
end
|
945
952
|
|
@@ -948,7 +955,7 @@ class Guest < ActiveRecord::Base
|
|
948
955
|
end
|
949
956
|
|
950
957
|
class Tag < ActiveRecord::Base
|
951
|
-
belongs_to :
|
958
|
+
belongs_to :article
|
952
959
|
end
|
953
960
|
```
|
954
961
|
|
@@ -957,64 +964,64 @@ Now all of the following will produce the expected join queries using `INNER JOI
|
|
957
964
|
#### Joining a Single Association
|
958
965
|
|
959
966
|
```ruby
|
960
|
-
Category.joins(:
|
967
|
+
Category.joins(:articles)
|
961
968
|
```
|
962
969
|
|
963
970
|
This produces:
|
964
971
|
|
965
972
|
```sql
|
966
973
|
SELECT categories.* FROM categories
|
967
|
-
INNER JOIN
|
974
|
+
INNER JOIN articles ON articles.category_id = categories.id
|
968
975
|
```
|
969
976
|
|
970
|
-
Or, in English: "return a Category object for all categories with
|
977
|
+
Or, in English: "return a Category object for all categories with articles". Note that you will see duplicate categories if more than one article has the same category. If you want unique categories, you can use `Category.joins(:articles).uniq`.
|
971
978
|
|
972
979
|
#### Joining Multiple Associations
|
973
980
|
|
974
981
|
```ruby
|
975
|
-
|
982
|
+
Article.joins(:category, :comments)
|
976
983
|
```
|
977
984
|
|
978
985
|
This produces:
|
979
986
|
|
980
987
|
```sql
|
981
|
-
SELECT
|
982
|
-
INNER JOIN categories ON
|
983
|
-
INNER JOIN comments ON comments.
|
988
|
+
SELECT articles.* FROM articles
|
989
|
+
INNER JOIN categories ON articles.category_id = categories.id
|
990
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
984
991
|
```
|
985
992
|
|
986
|
-
Or, in English: "return all
|
993
|
+
Or, in English: "return all articles that have a category and at least one comment". Note again that articles with multiple comments will show up multiple times.
|
987
994
|
|
988
995
|
#### Joining Nested Associations (Single Level)
|
989
996
|
|
990
997
|
```ruby
|
991
|
-
|
998
|
+
Article.joins(comments: :guest)
|
992
999
|
```
|
993
1000
|
|
994
1001
|
This produces:
|
995
1002
|
|
996
1003
|
```sql
|
997
|
-
SELECT
|
998
|
-
INNER JOIN comments ON comments.
|
1004
|
+
SELECT articles.* FROM articles
|
1005
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
999
1006
|
INNER JOIN guests ON guests.comment_id = comments.id
|
1000
1007
|
```
|
1001
1008
|
|
1002
|
-
Or, in English: "return all
|
1009
|
+
Or, in English: "return all articles that have a comment made by a guest."
|
1003
1010
|
|
1004
1011
|
#### Joining Nested Associations (Multiple Level)
|
1005
1012
|
|
1006
1013
|
```ruby
|
1007
|
-
Category.joins(
|
1014
|
+
Category.joins(articles: [{ comments: :guest }, :tags])
|
1008
1015
|
```
|
1009
1016
|
|
1010
1017
|
This produces:
|
1011
1018
|
|
1012
1019
|
```sql
|
1013
1020
|
SELECT categories.* FROM categories
|
1014
|
-
INNER JOIN
|
1015
|
-
INNER JOIN comments ON comments.
|
1021
|
+
INNER JOIN articles ON articles.category_id = categories.id
|
1022
|
+
INNER JOIN comments ON comments.article_id = articles.id
|
1016
1023
|
INNER JOIN guests ON guests.comment_id = comments.id
|
1017
|
-
INNER JOIN tags ON tags.
|
1024
|
+
INNER JOIN tags ON tags.article_id = articles.id
|
1018
1025
|
```
|
1019
1026
|
|
1020
1027
|
### Specifying Conditions on the Joined Tables
|
@@ -1030,7 +1037,7 @@ An alternative and cleaner syntax is to nest the hash conditions:
|
|
1030
1037
|
|
1031
1038
|
```ruby
|
1032
1039
|
time_range = (Time.now.midnight - 1.day)..Time.now.midnight
|
1033
|
-
Client.joins(:orders).where(orders: {created_at: time_range})
|
1040
|
+
Client.joins(:orders).where(orders: { created_at: time_range })
|
1034
1041
|
```
|
1035
1042
|
|
1036
1043
|
This will find all clients who have orders that were created yesterday, again using a `BETWEEN` SQL expression.
|
@@ -1083,18 +1090,18 @@ Active Record lets you eager load any number of associations with a single `Mode
|
|
1083
1090
|
#### Array of Multiple Associations
|
1084
1091
|
|
1085
1092
|
```ruby
|
1086
|
-
|
1093
|
+
Article.includes(:category, :comments)
|
1087
1094
|
```
|
1088
1095
|
|
1089
|
-
This loads all the
|
1096
|
+
This loads all the articles and the associated category and comments for each article.
|
1090
1097
|
|
1091
1098
|
#### Nested Associations Hash
|
1092
1099
|
|
1093
1100
|
```ruby
|
1094
|
-
Category.includes(
|
1101
|
+
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
|
1095
1102
|
```
|
1096
1103
|
|
1097
|
-
This will find the category with id 1 and eager load all of the associated
|
1104
|
+
This will find the category with id 1 and eager load all of the associated articles, the associated articles' tags and comments, and every comment's guest association.
|
1098
1105
|
|
1099
1106
|
### Specifying Conditions on Eager Loaded Associations
|
1100
1107
|
|
@@ -1103,18 +1110,31 @@ Even though Active Record lets you specify conditions on the eager loaded associ
|
|
1103
1110
|
However if you must do this, you may use `where` as you would normally.
|
1104
1111
|
|
1105
1112
|
```ruby
|
1106
|
-
|
1113
|
+
Article.includes(:comments).where(comments: { visible: true })
|
1107
1114
|
```
|
1108
1115
|
|
1109
|
-
This would generate a query which contains a `LEFT OUTER JOIN` whereas the
|
1116
|
+
This would generate a query which contains a `LEFT OUTER JOIN` whereas the
|
1117
|
+
`joins` method would generate one using the `INNER JOIN` function instead.
|
1110
1118
|
|
1111
1119
|
```ruby
|
1112
|
-
SELECT "
|
1120
|
+
SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1)
|
1113
1121
|
```
|
1114
1122
|
|
1115
1123
|
If there was no `where` condition, this would generate the normal set of two queries.
|
1116
1124
|
|
1117
|
-
|
1125
|
+
NOTE: Using `where` like this will only work when you pass it a Hash. For
|
1126
|
+
SQL-fragments you need use `references` to force joined tables:
|
1127
|
+
|
1128
|
+
```ruby
|
1129
|
+
Article.includes(:comments).where("comments.visible = true").references(:comments)
|
1130
|
+
```
|
1131
|
+
|
1132
|
+
If, in the case of this `includes` query, there were no comments for any
|
1133
|
+
articles, all the articles would still be loaded. By using `joins` (an INNER
|
1134
|
+
JOIN), the join conditions **must** match, otherwise no records will be
|
1135
|
+
returned.
|
1136
|
+
|
1137
|
+
|
1118
1138
|
|
1119
1139
|
Scopes
|
1120
1140
|
------
|
@@ -1124,7 +1144,7 @@ Scoping allows you to specify commonly-used queries which can be referenced as m
|
|
1124
1144
|
To define a simple scope, we use the `scope` method inside the class, passing the query that we'd like to run when this scope is called:
|
1125
1145
|
|
1126
1146
|
```ruby
|
1127
|
-
class
|
1147
|
+
class Article < ActiveRecord::Base
|
1128
1148
|
scope :published, -> { where(published: true) }
|
1129
1149
|
end
|
1130
1150
|
```
|
@@ -1132,7 +1152,7 @@ end
|
|
1132
1152
|
This is exactly the same as defining a class method, and which you use is a matter of personal preference:
|
1133
1153
|
|
1134
1154
|
```ruby
|
1135
|
-
class
|
1155
|
+
class Article < ActiveRecord::Base
|
1136
1156
|
def self.published
|
1137
1157
|
where(published: true)
|
1138
1158
|
end
|
@@ -1142,7 +1162,7 @@ end
|
|
1142
1162
|
Scopes are also chainable within scopes:
|
1143
1163
|
|
1144
1164
|
```ruby
|
1145
|
-
class
|
1165
|
+
class Article < ActiveRecord::Base
|
1146
1166
|
scope :published, -> { where(published: true) }
|
1147
1167
|
scope :published_and_commented, -> { published.where("comments_count > 0") }
|
1148
1168
|
end
|
@@ -1151,14 +1171,14 @@ end
|
|
1151
1171
|
To call this `published` scope we can call it on either the class:
|
1152
1172
|
|
1153
1173
|
```ruby
|
1154
|
-
|
1174
|
+
Article.published # => [published articles]
|
1155
1175
|
```
|
1156
1176
|
|
1157
|
-
Or on an association consisting of `
|
1177
|
+
Or on an association consisting of `Article` objects:
|
1158
1178
|
|
1159
1179
|
```ruby
|
1160
1180
|
category = Category.first
|
1161
|
-
category.
|
1181
|
+
category.articles.published # => [published articles belonging to this category]
|
1162
1182
|
```
|
1163
1183
|
|
1164
1184
|
### Passing in arguments
|
@@ -1166,21 +1186,21 @@ category.posts.published # => [published posts belonging to this category]
|
|
1166
1186
|
Your scope can take arguments:
|
1167
1187
|
|
1168
1188
|
```ruby
|
1169
|
-
class
|
1189
|
+
class Article < ActiveRecord::Base
|
1170
1190
|
scope :created_before, ->(time) { where("created_at < ?", time) }
|
1171
1191
|
end
|
1172
1192
|
```
|
1173
1193
|
|
1174
|
-
|
1194
|
+
Call the scope as if it were a class method:
|
1175
1195
|
|
1176
1196
|
```ruby
|
1177
|
-
|
1197
|
+
Article.created_before(Time.zone.now)
|
1178
1198
|
```
|
1179
1199
|
|
1180
1200
|
However, this is just duplicating the functionality that would be provided to you by a class method.
|
1181
1201
|
|
1182
1202
|
```ruby
|
1183
|
-
class
|
1203
|
+
class Article < ActiveRecord::Base
|
1184
1204
|
def self.created_before(time)
|
1185
1205
|
where("created_at < ?", time)
|
1186
1206
|
end
|
@@ -1190,7 +1210,36 @@ end
|
|
1190
1210
|
Using a class method is the preferred way to accept arguments for scopes. These methods will still be accessible on the association objects:
|
1191
1211
|
|
1192
1212
|
```ruby
|
1193
|
-
category.
|
1213
|
+
category.articles.created_before(time)
|
1214
|
+
```
|
1215
|
+
|
1216
|
+
### Applying a default scope
|
1217
|
+
|
1218
|
+
If we wish for a scope to be applied across all queries to the model we can use the
|
1219
|
+
`default_scope` method within the model itself.
|
1220
|
+
|
1221
|
+
```ruby
|
1222
|
+
class Client < ActiveRecord::Base
|
1223
|
+
default_scope { where("removed_at IS NULL") }
|
1224
|
+
end
|
1225
|
+
```
|
1226
|
+
|
1227
|
+
When queries are executed on this model, the SQL query will now look something like
|
1228
|
+
this:
|
1229
|
+
|
1230
|
+
```sql
|
1231
|
+
SELECT * FROM clients WHERE removed_at IS NULL
|
1232
|
+
```
|
1233
|
+
|
1234
|
+
If you need to do more complex things with a default scope, you can alternatively
|
1235
|
+
define it as a class method:
|
1236
|
+
|
1237
|
+
```ruby
|
1238
|
+
class Client < ActiveRecord::Base
|
1239
|
+
def self.default_scope
|
1240
|
+
# Should return an ActiveRecord::Relation.
|
1241
|
+
end
|
1242
|
+
end
|
1194
1243
|
```
|
1195
1244
|
|
1196
1245
|
### Merging of scopes
|
@@ -1203,80 +1252,49 @@ class User < ActiveRecord::Base
|
|
1203
1252
|
scope :inactive, -> { where state: 'inactive' }
|
1204
1253
|
end
|
1205
1254
|
|
1206
|
-
```ruby
|
1207
1255
|
User.active.inactive
|
1208
|
-
#
|
1256
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive'
|
1209
1257
|
```
|
1210
1258
|
|
1211
1259
|
We can mix and match `scope` and `where` conditions and the final sql
|
1212
|
-
will have all conditions joined with `AND
|
1260
|
+
will have all conditions joined with `AND`.
|
1213
1261
|
|
1214
1262
|
```ruby
|
1215
1263
|
User.active.where(state: 'finished')
|
1216
|
-
#
|
1264
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'finished'
|
1217
1265
|
```
|
1218
1266
|
|
1219
1267
|
If we do want the `last where clause` to win then `Relation#merge` can
|
1220
|
-
be used
|
1268
|
+
be used.
|
1221
1269
|
|
1222
1270
|
```ruby
|
1223
1271
|
User.active.merge(User.inactive)
|
1224
|
-
#
|
1272
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'
|
1225
1273
|
```
|
1226
1274
|
|
1227
|
-
One important caveat is that `default_scope` will be
|
1275
|
+
One important caveat is that `default_scope` will be prepended in
|
1228
1276
|
`scope` and `where` conditions.
|
1229
1277
|
|
1230
1278
|
```ruby
|
1231
1279
|
class User < ActiveRecord::Base
|
1232
|
-
default_scope
|
1280
|
+
default_scope { where state: 'pending' }
|
1233
1281
|
scope :active, -> { where state: 'active' }
|
1234
1282
|
scope :inactive, -> { where state: 'inactive' }
|
1235
1283
|
end
|
1236
1284
|
|
1237
1285
|
User.all
|
1238
|
-
#
|
1286
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
|
1239
1287
|
|
1240
1288
|
User.active
|
1241
|
-
#
|
1289
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active'
|
1242
1290
|
|
1243
1291
|
User.where(state: 'inactive')
|
1244
|
-
#
|
1292
|
+
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive'
|
1245
1293
|
```
|
1246
1294
|
|
1247
|
-
As you can see above the `default_scope` is being
|
1295
|
+
As you can see above the `default_scope` is being merged in both
|
1248
1296
|
`scope` and `where` conditions.
|
1249
1297
|
|
1250
|
-
|
1251
|
-
### Applying a default scope
|
1252
|
-
|
1253
|
-
If we wish for a scope to be applied across all queries to the model we can use the
|
1254
|
-
`default_scope` method within the model itself.
|
1255
|
-
|
1256
|
-
```ruby
|
1257
|
-
class Client < ActiveRecord::Base
|
1258
|
-
default_scope { where("removed_at IS NULL") }
|
1259
|
-
end
|
1260
|
-
```
|
1261
|
-
|
1262
|
-
When queries are executed on this model, the SQL query will now look something like
|
1263
|
-
this:
|
1264
|
-
|
1265
|
-
```sql
|
1266
|
-
SELECT * FROM clients WHERE removed_at IS NULL
|
1267
|
-
```
|
1268
|
-
|
1269
|
-
If you need to do more complex things with a default scope, you can alternatively
|
1270
|
-
define it as a class method:
|
1271
|
-
|
1272
|
-
```ruby
|
1273
|
-
class Client < ActiveRecord::Base
|
1274
|
-
def self.default_scope
|
1275
|
-
# Should return an ActiveRecord::Relation.
|
1276
|
-
end
|
1277
|
-
end
|
1278
|
-
```
|
1279
|
-
|
1280
1298
|
### Removing All Scoping
|
1281
1299
|
|
1282
1300
|
If we wish to remove scoping for any reason we can use the `unscoped` method. This is
|
@@ -1284,7 +1302,7 @@ especially useful if a `default_scope` is specified in the model and should not
|
|
1284
1302
|
applied for this particular query.
|
1285
1303
|
|
1286
1304
|
```ruby
|
1287
|
-
Client.unscoped.
|
1305
|
+
Client.unscoped.load
|
1288
1306
|
```
|
1289
1307
|
|
1290
1308
|
This method removes all scoping and will do a normal query on the table.
|
@@ -1301,11 +1319,6 @@ Client.unscoped {
|
|
1301
1319
|
Dynamic Finders
|
1302
1320
|
---------------
|
1303
1321
|
|
1304
|
-
NOTE: Dynamic finders have been deprecated in Rails 4.0 and will be
|
1305
|
-
removed in Rails 4.1. The best practice is to use Active Record scopes
|
1306
|
-
instead. You can find the deprecation gem at
|
1307
|
-
https://github.com/rails/activerecord-deprecated_finders
|
1308
|
-
|
1309
1322
|
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` for free from Active Record. If you have a `locked` field on the `Client` model, you also get `find_by_locked` and methods.
|
1310
1323
|
|
1311
1324
|
You can specify an exclamation point (`!`) 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")`
|
@@ -1315,6 +1328,11 @@ If you want to find both by name and locked, you can chain these finders togethe
|
|
1315
1328
|
Find or Build a New Object
|
1316
1329
|
--------------------------
|
1317
1330
|
|
1331
|
+
NOTE: Some dynamic finders have been deprecated in Rails 4.0 and will be
|
1332
|
+
removed in Rails 4.1. The best practice is to use Active Record scopes
|
1333
|
+
instead. You can find the deprecation gem at
|
1334
|
+
https://github.com/rails/activerecord-deprecated_finders
|
1335
|
+
|
1318
1336
|
It's common that you need to find a record or create it if it doesn't exist. You can do that with the `find_or_create_by` and `find_or_create_by!` methods.
|
1319
1337
|
|
1320
1338
|
### `find_or_create_by`
|
@@ -1341,7 +1359,7 @@ COMMIT
|
|
1341
1359
|
|
1342
1360
|
The new record might not be saved to the database; that depends on whether validations passed or not (just like `create`).
|
1343
1361
|
|
1344
|
-
Suppose we want to set the 'locked' attribute to
|
1362
|
+
Suppose we want to set the 'locked' attribute to `false` if we're
|
1345
1363
|
creating a new record, but we don't want to include it in the query. So
|
1346
1364
|
we want to find the client named "Andy", or if that client doesn't
|
1347
1365
|
exist, create a client named "Andy" which is not locked.
|
@@ -1418,7 +1436,12 @@ If you'd like to use your own SQL to find records in a table you can use `find_b
|
|
1418
1436
|
```ruby
|
1419
1437
|
Client.find_by_sql("SELECT * FROM clients
|
1420
1438
|
INNER JOIN orders ON clients.id = orders.client_id
|
1421
|
-
ORDER clients.created_at desc")
|
1439
|
+
ORDER BY clients.created_at desc")
|
1440
|
+
# => [
|
1441
|
+
#<Client id: 1, first_name: "Lucas" >,
|
1442
|
+
#<Client id: 2, first_name: "Jan" >,
|
1443
|
+
# ...
|
1444
|
+
]
|
1422
1445
|
```
|
1423
1446
|
|
1424
1447
|
`find_by_sql` provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
|
@@ -1428,12 +1451,16 @@ Client.find_by_sql("SELECT * FROM clients
|
|
1428
1451
|
`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
|
1429
1452
|
|
1430
1453
|
```ruby
|
1431
|
-
Client.connection.select_all("SELECT
|
1454
|
+
Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'")
|
1455
|
+
# => [
|
1456
|
+
{"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
|
1457
|
+
{"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
|
1458
|
+
]
|
1432
1459
|
```
|
1433
1460
|
|
1434
1461
|
### `pluck`
|
1435
1462
|
|
1436
|
-
`pluck` can be used to query
|
1463
|
+
`pluck` can be used to query single or multiple columns from the underlying table of a model. It accepts a list of column names as argument and returns an array of values of the specified columns with the corresponding data type.
|
1437
1464
|
|
1438
1465
|
```ruby
|
1439
1466
|
Client.where(active: true).pluck(:id)
|
@@ -1449,7 +1476,7 @@ Client.pluck(:id, :name)
|
|
1449
1476
|
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
1450
1477
|
```
|
1451
1478
|
|
1452
|
-
`pluck` makes it possible to replace code like
|
1479
|
+
`pluck` makes it possible to replace code like:
|
1453
1480
|
|
1454
1481
|
```ruby
|
1455
1482
|
Client.select(:id).map { |c| c.id }
|
@@ -1459,7 +1486,7 @@ Client.select(:id).map(&:id)
|
|
1459
1486
|
Client.select(:id, :name).map { |c| [c.id, c.name] }
|
1460
1487
|
```
|
1461
1488
|
|
1462
|
-
with
|
1489
|
+
with:
|
1463
1490
|
|
1464
1491
|
```ruby
|
1465
1492
|
Client.pluck(:id)
|
@@ -1467,6 +1494,37 @@ Client.pluck(:id)
|
|
1467
1494
|
Client.pluck(:id, :name)
|
1468
1495
|
```
|
1469
1496
|
|
1497
|
+
Unlike `select`, `pluck` directly converts a database result into a Ruby `Array`,
|
1498
|
+
without constructing `ActiveRecord` objects. This can mean better performance for
|
1499
|
+
a large or often-running query. However, any model method overrides will
|
1500
|
+
not be available. For example:
|
1501
|
+
|
1502
|
+
```ruby
|
1503
|
+
class Client < ActiveRecord::Base
|
1504
|
+
def name
|
1505
|
+
"I am #{super}"
|
1506
|
+
end
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
Client.select(:name).map &:name
|
1510
|
+
# => ["I am David", "I am Jeremy", "I am Jose"]
|
1511
|
+
|
1512
|
+
Client.pluck(:name)
|
1513
|
+
# => ["David", "Jeremy", "Jose"]
|
1514
|
+
```
|
1515
|
+
|
1516
|
+
Furthermore, unlike `select` and other `Relation` scopes, `pluck` triggers an immediate
|
1517
|
+
query, and thus cannot be chained with any further scopes, although it can work with
|
1518
|
+
scopes already constructed earlier:
|
1519
|
+
|
1520
|
+
```ruby
|
1521
|
+
Client.pluck(:name).limit(1)
|
1522
|
+
# => NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>
|
1523
|
+
|
1524
|
+
Client.limit(1).pluck(:name)
|
1525
|
+
# => ["David"]
|
1526
|
+
```
|
1527
|
+
|
1470
1528
|
### `ids`
|
1471
1529
|
|
1472
1530
|
`ids` can be used to pluck all the IDs for the relation using the table's primary key.
|
@@ -1488,18 +1546,21 @@ Person.ids
|
|
1488
1546
|
Existence of Objects
|
1489
1547
|
--------------------
|
1490
1548
|
|
1491
|
-
If you simply want to check for the existence of the object there's a method called `exists?`.
|
1549
|
+
If you simply want to check for the existence of the object there's a method called `exists?`.
|
1550
|
+
This method will query the database using the same query as `find`, but instead of returning an
|
1551
|
+
object or collection of objects it will return either `true` or `false`.
|
1492
1552
|
|
1493
1553
|
```ruby
|
1494
1554
|
Client.exists?(1)
|
1495
1555
|
```
|
1496
1556
|
|
1497
|
-
The `exists?` method also takes multiple
|
1557
|
+
The `exists?` method also takes multiple values, but the catch is that it will return `true` if any
|
1558
|
+
one of those records exists.
|
1498
1559
|
|
1499
1560
|
```ruby
|
1500
|
-
Client.exists?(1,2,3)
|
1561
|
+
Client.exists?(id: [1,2,3])
|
1501
1562
|
# or
|
1502
|
-
Client.exists?([
|
1563
|
+
Client.exists?(name: ['John', 'Sergei'])
|
1503
1564
|
```
|
1504
1565
|
|
1505
1566
|
It's even possible to use `exists?` without any arguments on a model or a relation.
|
@@ -1508,7 +1569,8 @@ It's even possible to use `exists?` without any arguments on a model or a relati
|
|
1508
1569
|
Client.where(first_name: 'Ryan').exists?
|
1509
1570
|
```
|
1510
1571
|
|
1511
|
-
The above returns `true` if there is at least one client with the `first_name` 'Ryan' and `false`
|
1572
|
+
The above returns `true` if there is at least one client with the `first_name` 'Ryan' and `false`
|
1573
|
+
otherwise.
|
1512
1574
|
|
1513
1575
|
```ruby
|
1514
1576
|
Client.exists?
|
@@ -1520,20 +1582,20 @@ You can also use `any?` and `many?` to check for existence on a model or relatio
|
|
1520
1582
|
|
1521
1583
|
```ruby
|
1522
1584
|
# via a model
|
1523
|
-
|
1524
|
-
|
1585
|
+
Article.any?
|
1586
|
+
Article.many?
|
1525
1587
|
|
1526
1588
|
# via a named scope
|
1527
|
-
|
1528
|
-
|
1589
|
+
Article.recent.any?
|
1590
|
+
Article.recent.many?
|
1529
1591
|
|
1530
1592
|
# via a relation
|
1531
|
-
|
1532
|
-
|
1593
|
+
Article.where(published: true).any?
|
1594
|
+
Article.where(published: true).many?
|
1533
1595
|
|
1534
1596
|
# via an association
|
1535
|
-
|
1536
|
-
|
1597
|
+
Article.first.categories.any?
|
1598
|
+
Article.first.categories.many?
|
1537
1599
|
```
|
1538
1600
|
|
1539
1601
|
Calculations
|
@@ -1558,7 +1620,7 @@ Client.where(first_name: 'Ryan').count
|
|
1558
1620
|
You can also use various finder methods on a relation for performing complex calculations:
|
1559
1621
|
|
1560
1622
|
```ruby
|
1561
|
-
Client.includes("orders").where(first_name: 'Ryan', orders: {status: 'received'}).count
|
1623
|
+
Client.includes("orders").where(first_name: 'Ryan', orders: { status: 'received' }).count
|
1562
1624
|
```
|
1563
1625
|
|
1564
1626
|
Which will execute:
|
@@ -1623,19 +1685,26 @@ Running EXPLAIN
|
|
1623
1685
|
You can run EXPLAIN on the queries triggered by relations. For example,
|
1624
1686
|
|
1625
1687
|
```ruby
|
1626
|
-
User.where(id: 1).joins(:
|
1688
|
+
User.where(id: 1).joins(:articles).explain
|
1627
1689
|
```
|
1628
1690
|
|
1629
1691
|
may yield
|
1630
1692
|
|
1631
1693
|
```
|
1632
|
-
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `
|
1633
|
-
|
1634
|
-
| id | select_type | table
|
1635
|
-
|
1636
|
-
| 1 | SIMPLE | users
|
1637
|
-
| 1 | SIMPLE |
|
1638
|
-
|
1694
|
+
EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`user_id` = `users`.`id` WHERE `users`.`id` = 1
|
1695
|
+
+----+-------------+----------+-------+---------------+
|
1696
|
+
| id | select_type | table | type | possible_keys |
|
1697
|
+
+----+-------------+----------+-------+---------------+
|
1698
|
+
| 1 | SIMPLE | users | const | PRIMARY |
|
1699
|
+
| 1 | SIMPLE | articles | ALL | NULL |
|
1700
|
+
+----+-------------+----------+-------+---------------+
|
1701
|
+
+---------+---------+-------+------+-------------+
|
1702
|
+
| key | key_len | ref | rows | Extra |
|
1703
|
+
+---------+---------+-------+------+-------------+
|
1704
|
+
| PRIMARY | 4 | const | 1 | |
|
1705
|
+
| NULL | NULL | NULL | 1 | Using where |
|
1706
|
+
+---------+---------+-------+------+-------------+
|
1707
|
+
|
1639
1708
|
2 rows in set (0.00 sec)
|
1640
1709
|
```
|
1641
1710
|
|
@@ -1645,15 +1714,15 @@ Active Record performs a pretty printing that emulates the one of the database
|
|
1645
1714
|
shells. So, the same query running with the PostgreSQL adapter would yield instead
|
1646
1715
|
|
1647
1716
|
```
|
1648
|
-
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "
|
1717
|
+
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1
|
1649
1718
|
QUERY PLAN
|
1650
1719
|
------------------------------------------------------------------------------
|
1651
1720
|
Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
|
1652
|
-
Join Filter: (
|
1721
|
+
Join Filter: (articles.user_id = users.id)
|
1653
1722
|
-> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
|
1654
1723
|
Index Cond: (id = 1)
|
1655
|
-
-> Seq Scan on
|
1656
|
-
Filter: (
|
1724
|
+
-> Seq Scan on articles (cost=0.00..28.88 rows=8 width=4)
|
1725
|
+
Filter: (articles.user_id = 1)
|
1657
1726
|
(6 rows)
|
1658
1727
|
```
|
1659
1728
|
|
@@ -1662,26 +1731,39 @@ may need the results of previous ones. Because of that, `explain` actually
|
|
1662
1731
|
executes the query, and then asks for the query plans. For example,
|
1663
1732
|
|
1664
1733
|
```ruby
|
1665
|
-
User.where(id: 1).includes(:
|
1734
|
+
User.where(id: 1).includes(:articles).explain
|
1666
1735
|
```
|
1667
1736
|
|
1668
1737
|
yields
|
1669
1738
|
|
1670
1739
|
```
|
1671
1740
|
EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
|
1672
|
-
|
1673
|
-
| id | select_type | table | type | possible_keys |
|
1674
|
-
|
1675
|
-
| 1 | SIMPLE | users | const | PRIMARY |
|
1676
|
-
|
1741
|
+
+----+-------------+-------+-------+---------------+
|
1742
|
+
| id | select_type | table | type | possible_keys |
|
1743
|
+
+----+-------------+-------+-------+---------------+
|
1744
|
+
| 1 | SIMPLE | users | const | PRIMARY |
|
1745
|
+
+----+-------------+-------+-------+---------------+
|
1746
|
+
+---------+---------+-------+------+-------+
|
1747
|
+
| key | key_len | ref | rows | Extra |
|
1748
|
+
+---------+---------+-------+------+-------+
|
1749
|
+
| PRIMARY | 4 | const | 1 | |
|
1750
|
+
+---------+---------+-------+------+-------+
|
1751
|
+
|
1677
1752
|
1 row in set (0.00 sec)
|
1678
1753
|
|
1679
|
-
EXPLAIN for: SELECT `
|
1680
|
-
|
1681
|
-
| id | select_type | table
|
1682
|
-
|
1683
|
-
| 1 | SIMPLE |
|
1684
|
-
|
1754
|
+
EXPLAIN for: SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN (1)
|
1755
|
+
+----+-------------+----------+------+---------------+
|
1756
|
+
| id | select_type | table | type | possible_keys |
|
1757
|
+
+----+-------------+----------+------+---------------+
|
1758
|
+
| 1 | SIMPLE | articles | ALL | NULL |
|
1759
|
+
+----+-------------+----------+------+---------------+
|
1760
|
+
+------+---------+------+------+-------------+
|
1761
|
+
| key | key_len | ref | rows | Extra |
|
1762
|
+
+------+---------+------+------+-------------+
|
1763
|
+
| NULL | NULL | NULL | 1 | Using where |
|
1764
|
+
+------+---------+------+------+-------------+
|
1765
|
+
|
1766
|
+
|
1685
1767
|
1 row in set (0.00 sec)
|
1686
1768
|
```
|
1687
1769
|
|