aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/association_basics.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/association_basics.md')
-rw-r--r--guides/source/association_basics.md306
1 files changed, 179 insertions, 127 deletions
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 4977d4f30e..e076f10ece 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Associations
==========================
@@ -94,9 +94,9 @@ class Book < ApplicationRecord
end
```
-![belongs_to Association Diagram](images/belongs_to.png)
+![belongs_to Association Diagram](images/association_basics/belongs_to.png)
-NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
+NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model and tried to create the instance by `Book.create(authors: @author)`, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
The corresponding migration might look like this:
@@ -109,7 +109,7 @@ class CreateBooks < ActiveRecord::Migration[5.0]
end
create_table :books do |t|
- t.belongs_to :author, index: true
+ t.belongs_to :author
t.datetime :published_at
t.timestamps
end
@@ -127,7 +127,7 @@ class Supplier < ApplicationRecord
end
```
-![has_one Association Diagram](images/has_one.png)
+![has_one Association Diagram](images/association_basics/has_one.png)
The corresponding migration might look like this:
@@ -140,7 +140,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0]
end
create_table :accounts do |t|
- t.belongs_to :supplier, index: true
+ t.belongs_to :supplier
t.string :account_number
t.timestamps
end
@@ -154,7 +154,7 @@ case, the column definition might look like this:
```ruby
create_table :accounts do |t|
- t.belongs_to :supplier, index: true, unique: true, foreign_key: true
+ t.belongs_to :supplier, index: { unique: true }, foreign_key: true
# ...
end
```
@@ -171,7 +171,7 @@ end
NOTE: The name of the other model is pluralized when declaring a `has_many` association.
-![has_many Association Diagram](images/has_many.png)
+![has_many Association Diagram](images/association_basics/has_many.png)
The corresponding migration might look like this:
@@ -184,7 +184,7 @@ class CreateAuthors < ActiveRecord::Migration[5.0]
end
create_table :books do |t|
- t.belongs_to :author, index: true
+ t.belongs_to :author
t.datetime :published_at
t.timestamps
end
@@ -213,7 +213,7 @@ class Patient < ApplicationRecord
end
```
-![has_many :through Association Diagram](images/has_many_through.png)
+![has_many :through Association Diagram](images/association_basics/has_many_through.png)
The corresponding migration might look like this:
@@ -231,8 +231,8 @@ class CreateAppointments < ActiveRecord::Migration[5.0]
end
create_table :appointments do |t|
- t.belongs_to :physician, index: true
- t.belongs_to :patient, index: true
+ t.belongs_to :physician
+ t.belongs_to :patient
t.datetime :appointment_date
t.timestamps
end
@@ -299,7 +299,7 @@ class AccountHistory < ApplicationRecord
end
```
-![has_one :through Association Diagram](images/has_one_through.png)
+![has_one :through Association Diagram](images/association_basics/has_one_through.png)
The corresponding migration might look like this:
@@ -312,13 +312,13 @@ class CreateAccountHistories < ActiveRecord::Migration[5.0]
end
create_table :accounts do |t|
- t.belongs_to :supplier, index: true
+ t.belongs_to :supplier
t.string :account_number
t.timestamps
end
create_table :account_histories do |t|
- t.belongs_to :account, index: true
+ t.belongs_to :account
t.integer :credit_rating
t.timestamps
end
@@ -340,7 +340,7 @@ class Part < ApplicationRecord
end
```
-![has_and_belongs_to_many Association Diagram](images/habtm.png)
+![has_and_belongs_to_many Association Diagram](images/association_basics/habtm.png)
The corresponding migration might look like this:
@@ -358,8 +358,8 @@ class CreateAssembliesAndParts < ActiveRecord::Migration[5.0]
end
create_table :assemblies_parts, id: false do |t|
- t.belongs_to :assembly, index: true
- t.belongs_to :part, index: true
+ t.belongs_to :assembly
+ t.belongs_to :part
end
end
end
@@ -387,7 +387,7 @@ The corresponding migration might look like this:
class CreateSuppliers < ActiveRecord::Migration[5.0]
def change
create_table :suppliers do |t|
- t.string :name
+ t.string :name
t.timestamps
end
@@ -439,7 +439,7 @@ end
The simplest rule of thumb is that you should set up a `has_many :through` relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a `has_and_belongs_to_many` relationship (though you'll need to remember to create the joining table in the database).
-You should use `has_many :through` if you need validations, callbacks or extra attributes on the join model.
+You should use `has_many :through` if you need validations, callbacks, or extra attributes on the join model.
### Polymorphic Associations
@@ -487,14 +487,14 @@ class CreatePictures < ActiveRecord::Migration[5.0]
def change
create_table :pictures do |t|
t.string :name
- t.references :imageable, polymorphic: true, index: true
+ t.references :imageable, polymorphic: true
t.timestamps
end
end
end
```
-![Polymorphic Association Diagram](images/polymorphic.png)
+![Polymorphic Association Diagram](images/association_basics/polymorphic.png)
### Self Joins
@@ -505,7 +505,7 @@ class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
- belongs_to :manager, class_name: "Employee"
+ belongs_to :manager, class_name: "Employee", optional: true
end
```
@@ -517,7 +517,7 @@ In your migrations/schema, you will add a references column to the model itself.
class CreateEmployees < ActiveRecord::Migration[5.0]
def change
create_table :employees do |t|
- t.references :manager, index: true
+ t.references :manager
t.timestamps
end
end
@@ -550,8 +550,8 @@ But what if you want to reload the cache, because data might have been changed b
```ruby
author.books # retrieves books from the database
author.books.size # uses the cached copy of books
-author.books.reload.empty? # discards the cached copy of books
- # and goes back to the database
+author.books.reload.empty? # discards the cached copy of books
+ # and goes back to the database
```
### Avoiding Name Collisions
@@ -572,27 +572,35 @@ class Book < ApplicationRecord
end
```
-This declaration needs to be backed up by the proper foreign key declaration on the books table:
+This declaration needs to be backed up by a corresponding foreign key column in the books table. For a brand new table, the migration might look something like this:
```ruby
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
- t.datetime :published_at
- t.string :book_number
- t.integer :author_id
+ t.datetime :published_at
+ t.string :book_number
+ t.references :author
end
+ end
+end
+```
+
+Whereas for an existing table, it might look like this:
- add_index :books, :author_id
+```ruby
+class AddAuthorToBooks < ActiveRecord::Migration[5.0]
+ def change
+ add_reference :books, :author
end
end
```
-If you create an association some time after you build the underlying model, you need to remember to create an `add_column` migration to provide the necessary foreign key.
+NOTE: If you wish to [enforce referential integrity at the database level](/active_record_migrations.html#foreign-keys), add the `foreign_key: true` option to the ‘reference’ column declarations above.
#### Creating Join Tables for `has_and_belongs_to_many` Associations
-If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical book of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
+If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical order of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
WARNING: The precedence between model names is calculated using the `<=>` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '\_' is lexicographically _less_ than 's' in common encodings).
@@ -647,11 +655,11 @@ By default, associations look for objects only within the current module's scope
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account
+ has_one :account
end
class Account < ApplicationRecord
- belongs_to :supplier
+ belongs_to :supplier
end
end
end
@@ -663,13 +671,13 @@ This will work fine, because both the `Supplier` and the `Account` class are def
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account
+ has_one :account
end
end
module Billing
class Account < ApplicationRecord
- belongs_to :supplier
+ belongs_to :supplier
end
end
end
@@ -681,14 +689,14 @@ To associate a model with a model in a different namespace, you must specify the
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account,
+ has_one :account,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
- belongs_to :supplier,
+ belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
@@ -709,55 +717,64 @@ class Book < ApplicationRecord
end
```
-By default, Active Record doesn't know about the connection between these associations. This can lead to two copies of an object getting out of sync:
+Active Record will attempt to automatically identify that these two models share a bi-directional association based on the association name. In this way, Active Record will only load one copy of the `Author` object, making your application more efficient and preventing inconsistent data:
```ruby
a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
-a.first_name = 'Manny'
-a.first_name == b.author.first_name # => false
+a.first_name = 'David'
+a.first_name == b.author.first_name # => true
```
-This happens because `a` and `b.author` are two different in-memory representations of the same data, and neither one is automatically refreshed from changes to the other. Active Record provides the `:inverse_of` option so that you can inform it of these relations:
+Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain a scope or any of the following options:
+
+* `:through`
+* `:foreign_key`
+
+For example, consider the following model declarations:
```ruby
class Author < ApplicationRecord
- has_many :books, inverse_of: :author
+ has_many :books
end
class Book < ApplicationRecord
- belongs_to :author, inverse_of: :books
+ belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
```
-With these changes, Active Record will only load one copy of the author object, preventing inconsistencies and making your application more efficient:
+Active Record will no longer automatically recognize the bi-directional association:
```ruby
a = Author.first
b = a.books.first
-a.first_name == b.author.first_name # => true
-a.first_name = 'Manny'
-a.first_name == b.author.first_name # => true
+a.first_name == b.writer.first_name # => true
+a.first_name = 'David'
+a.first_name == b.writer.first_name # => false
```
-There are a few limitations to `inverse_of` support:
+Active Record provides the `:inverse_of` option so you can explicitly declare bi-directional associations:
-* They do not work with `:through` associations.
-* They do not work with `:polymorphic` associations.
-* They do not work with `:as` associations.
-* For `belongs_to` associations, `has_many` inverse associations are ignored.
+```ruby
+class Author < ApplicationRecord
+ has_many :books, inverse_of: 'writer'
+end
-Every association will attempt to automatically find the inverse association
-and set the `:inverse_of` option heuristically (based on the association name).
-Most associations with standard names will be supported. However, associations
-that contain the following options will not have their inverses set
-automatically:
+class Book < ApplicationRecord
+ belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
+end
+```
-* `:conditions`
-* `:through`
-* `:polymorphic`
-* `:foreign_key`
+By including the `:inverse_of` option in the `has_many` association declaration, Active Record will now recognize the bi-directional association:
+
+```ruby
+a = Author.first
+b = a.books.first
+a.first_name == b.writer.first_name # => true
+a.first_name = 'David'
+a.first_name == b.writer.first_name # => true
+```
Detailed Association Reference
------------------------------
@@ -770,13 +787,14 @@ The `belongs_to` association creates a one-to-one match with another model. In d
#### Methods Added by `belongs_to`
-When you declare a `belongs_to` association, the declaring class automatically gains five methods related to the association:
+When you declare a `belongs_to` association, the declaring class automatically gains 6 methods related to the association:
* `association`
* `association=(associate)`
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration:
@@ -794,6 +812,7 @@ author=
build_author
create_author
create_author!
+reload_author
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -806,10 +825,10 @@ The `association` method returns the associated object, if any. If no associated
@author = @book.author
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@author = @book.reload.author
+@author = @book.reload_author
```
##### `association=(associate)`
@@ -849,7 +868,7 @@ While Rails uses intelligent defaults that will work well in most situations, th
```ruby
class Book < ApplicationRecord
- belongs_to :author, dependent: :destroy,
+ belongs_to :author, touch: :books_updated_at,
counter_cache: true
end
```
@@ -870,7 +889,7 @@ The `belongs_to` association supports these options:
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -926,19 +945,18 @@ class Author < ApplicationRecord
end
```
-NOTE: You only need to specify the :counter_cache option on the `belongs_to`
+NOTE: You only need to specify the `:counter_cache` option on the `belongs_to`
side of the association.
Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`.
##### `:dependent`
-Controls what happens to associated objects when their owner is destroyed:
+If you set the `:dependent` option to:
-* `:destroy` causes the associated objects to also be destroyed.
-* `:delete_all` causes the associated objects to be deleted directly from the database (callbacks are not executed).
-* `:nullify` causes the foreign keys to be set to `NULL` (callbacks are not executed).
-* `:restrict_with_exception` causes an exception to be raised if there are associated records.
-* `:restrict_with_error` causes an error to be added to the owner if there are associated objects.
+* `:destroy`, when the object is destroyed, `destroy` will be called on its
+associated objects.
+* `:delete`, when the object is destroyed, all its associated objects will be
+deleted directly from the database without calling their `destroy` method.
WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database.
@@ -977,7 +995,7 @@ When we execute `@user.todos.create` then the `@todo` record will have its
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `has_many` or `has_one` association that is the inverse of this association. Does not work in combination with the `:polymorphic` options.
+The `:inverse_of` option specifies the name of the `has_many` or `has_one` association that is the inverse of this association.
```ruby
class Author < ApplicationRecord
@@ -1007,7 +1025,7 @@ class Author < ApplicationRecord
end
```
-In this case, saving or destroying an book will update the timestamp on the associated author. You can also specify a particular timestamp attribute to update:
+In this case, saving or destroying a book will update the timestamp on the associated author. You can also specify a particular timestamp attribute to update:
```ruby
class Book < ApplicationRecord
@@ -1030,8 +1048,7 @@ There may be times when you wish to customize the query used by `belongs_to`. Su
```ruby
class Book < ApplicationRecord
- belongs_to :author, -> { where active: true },
- dependent: :destroy
+ belongs_to :author, -> { where active: true }
end
```
@@ -1047,7 +1064,7 @@ You can use any of the standard [querying methods](active_record_querying.html)
The `where` method lets you specify the conditions that the associated object must meet.
```ruby
-class book < ApplicationRecord
+class Book < ApplicationRecord
belongs_to :author, -> { where active: true }
end
```
@@ -1057,13 +1074,13 @@ end
You can use the `includes` method to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
```ruby
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
class Author < ApplicationRecord
@@ -1071,16 +1088,16 @@ class Author < ApplicationRecord
end
```
-If you frequently retrieve authors directly from line items (`@line_item.book.author`), then you can make your code somewhat more efficient by including authors in the association from line items to books:
+If you frequently retrieve authors directly from chapters (`@chapter.book.author`), then you can make your code somewhat more efficient by including authors in the association from chapters to books:
```ruby
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book, -> { includes :author }
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
class Author < ApplicationRecord
@@ -1120,13 +1137,14 @@ The `has_one` association creates a one-to-one match with another model. In data
#### Methods Added by `has_one`
-When you declare a `has_one` association, the declaring class automatically gains five methods related to the association:
+When you declare a `has_one` association, the declaring class automatically gains 6 methods related to the association:
* `association`
* `association=(associate)`
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration:
@@ -1144,6 +1162,7 @@ account=
build_account
create_account
create_account!
+reload_account
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -1156,10 +1175,10 @@ The `association` method returns the associated object, if any. If no associated
@account = @supplier.account
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@account = @supplier.reload.account
+@account = @supplier.reload_account
```
##### `association=(associate)`
@@ -1220,7 +1239,7 @@ Setting the `:as` option indicates that this is a polymorphic association. Polym
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -1238,8 +1257,8 @@ Controls what happens to the associated object when its owner is destroyed:
* `:destroy` causes the associated object to also be destroyed
* `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute)
-* `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed.
-* `:restrict_with_exception` causes an exception to be raised if there is an associated record
+* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed.
+* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there is an associated record
* `:restrict_with_error` causes an error to be added to the owner if there is an associated object
It's necessary not to set or leave `:nullify` option for those associations
@@ -1262,7 +1281,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options.
+The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association.
```ruby
class Supplier < ApplicationRecord
@@ -1383,7 +1402,7 @@ If either of these saves fails due to validation errors, then the assignment sta
If the parent object (the one declaring the `has_one` association) is unsaved (that is, `new_record?` returns `true`) then the child objects are not saved. They will automatically when the parent object is saved.
-If you want to assign an object to a `has_one` association without saving the object, use the `association.build` method.
+If you want to assign an object to a `has_one` association without saving the object, use the `build_association` method.
### `has_many` Association Reference
@@ -1391,7 +1410,7 @@ The `has_many` association creates a one-to-many relationship with another model
#### Methods Added by `has_many`
-When you declare a `has_many` association, the declaring class automatically gains 16 methods related to the association:
+When you declare a `has_many` association, the declaring class automatically gains 17 methods related to the association:
* `collection`
* `collection<<(object, ...)`
@@ -1409,6 +1428,7 @@ When you declare a `has_many` association, the declaring class automatically gai
* `collection.build(attributes = {}, ...)`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1437,11 +1457,12 @@ books.exists?(...)
books.build(attributes = {}, ...)
books.create(attributes = {})
books.create!(attributes = {})
+books.reload
```
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@books = @author.books
@@ -1477,7 +1498,7 @@ WARNING: Objects will _always_ be removed from the database, ignoring the `:depe
##### `collection=(objects)`
-The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection_singular_ids`
@@ -1489,7 +1510,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects
##### `collection_singular_ids=(ids)`
-The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection.clear`
@@ -1522,10 +1543,11 @@ The `collection.size` method returns the number of objects in the collection.
##### `collection.find(...)`
-The `collection.find` method finds objects within the collection. It uses the same syntax and options as `ActiveRecord::Base.find`.
+The `collection.find` method finds objects within the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
-@available_books = @author.books.find(1)
+@available_book = @author.books.find(1)
```
##### `collection.where(...)`
@@ -1541,7 +1563,7 @@ The `collection.where` method finds objects within the collection based on the c
The `collection.exists?` method checks whether an object meeting the supplied
conditions exists in the collection. It uses the same syntax and options as
-[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
+[`ActiveRecord::Base.exists?`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
##### `collection.build(attributes = {}, ...)`
@@ -1575,6 +1597,14 @@ The `collection.create` method returns a single or array of new objects of the a
Does the same as `collection.create` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@books = @author.books.reload
+```
+
#### Options for `has_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
@@ -1606,7 +1636,7 @@ Setting the `:as` option indicates that this is a polymorphic association, as di
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -1628,10 +1658,12 @@ Controls what happens to the associated objects when their owner is destroyed:
* `:destroy` causes all the associated objects to also be destroyed
* `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute)
-* `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed.
-* `:restrict_with_exception` causes an exception to be raised if there are any associated records
+* `:nullify` causes the foreign key to be set to `NULL`. Polymorphic type column is also nullified on polymorphic associations. Callbacks are not executed.
+* `:restrict_with_exception` causes an `ActiveRecord::DeleteRestrictionError` exception to be raised if there are any associated records
* `:restrict_with_error` causes an error to be added to the owner if there are any associated objects
+The `:destroy` and `:delete_all` options also affect the semantics of the `collection.delete` and `collection=` methods by causing them to destroy associated objects when they are removed from the collection.
+
##### `:foreign_key`
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix `_id` added. The `:foreign_key` option lets you set the name of the foreign key directly:
@@ -1646,7 +1678,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options.
+The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association.
```ruby
class Author < ApplicationRecord
@@ -1748,8 +1780,8 @@ The `group` method supplies an attribute name to group the result set by, using
```ruby
class Author < ApplicationRecord
- has_many :line_items, -> { group 'books.id' },
- through: :books
+ has_many :chapters, -> { group 'books.id' },
+ through: :books
end
```
@@ -1764,27 +1796,27 @@ end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
```
-If you frequently retrieve line items directly from authors (`@author.books.line_items`), then you can make your code somewhat more efficient by including line items in the association from authors to books:
+If you frequently retrieve chapters directly from authors (`@author.books.chapters`), then you can make your code somewhat more efficient by including chapters in the association from authors to books:
```ruby
class Author < ApplicationRecord
- has_many :books, -> { includes :line_items }
+ has_many :books, -> { includes :chapters }
end
class Book < ApplicationRecord
belongs_to :author
- has_many :line_items
+ has_many :chapters
end
-class LineItem < ApplicationRecord
+class Chapter < ApplicationRecord
belongs_to :book
end
```
@@ -1797,7 +1829,7 @@ The `limit` method lets you restrict the total number of objects that will be fe
class Author < ApplicationRecord
has_many :recent_books,
-> { order('published_at desc').limit(100) },
- class_name: "Book",
+ class_name: "Book"
end
```
@@ -1841,7 +1873,7 @@ article = Article.create(name: 'a1')
person.articles << article
person.articles << article
person.articles.inspect # => [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">]
-Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
+Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
```
In the above case there are two readings and `person.articles` brings out both of
@@ -1860,7 +1892,7 @@ article = Article.create(name: 'a1')
person.articles << article
person.articles << article
person.articles.inspect # => [#<Article id: 7, name: "a1">]
-Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
+Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
```
In the above case there are still two readings. However `person.articles` shows
@@ -1913,7 +1945,7 @@ The `has_and_belongs_to_many` association creates a many-to-many relationship wi
#### Methods Added by `has_and_belongs_to_many`
-When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 16 methods related to the association:
+When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 17 methods related to the association:
* `collection`
* `collection<<(object, ...)`
@@ -1931,6 +1963,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au
* `collection.build(attributes = {})`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1959,6 +1992,7 @@ assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
assemblies.create!(attributes = {})
+assemblies.reload
```
##### Additional Column Methods
@@ -1970,7 +2004,7 @@ WARNING: The use of extra attributes on the join table in a `has_and_belongs_to_
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@assemblies = @part.assemblies
@@ -1994,11 +2028,9 @@ The `collection.delete` method removes one or more objects from the collection b
@part.assemblies.delete(@assembly1)
```
-WARNING: This does not trigger callbacks on the join records.
-
##### `collection.destroy(object, ...)`
-The `collection.destroy` method removes one or more objects from the collection by running `destroy` on each record in the join table, including running callbacks. This does not destroy the objects.
+The `collection.destroy` method removes one or more objects from the collection by deleting records in the join table. This does not destroy the objects.
```ruby
@part.assemblies.destroy(@assembly1)
@@ -2006,7 +2038,7 @@ The `collection.destroy` method removes one or more objects from the collection
##### `collection=(objects)`
-The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection_singular_ids`
@@ -2018,7 +2050,7 @@ The `collection_singular_ids` method returns an array of the ids of the objects
##### `collection_singular_ids=(ids)`
-The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate.
+The `collection_singular_ids=` method makes the collection contain only the objects identified by the supplied primary key values, by adding and deleting as appropriate. The changes are persisted to the database.
##### `collection.clear`
@@ -2044,7 +2076,8 @@ The `collection.size` method returns the number of objects in the collection.
##### `collection.find(...)`
-The `collection.find` method finds objects within the collection. It uses the same syntax and options as `ActiveRecord::Base.find`. It also adds the additional condition that the object must be in the collection.
+The `collection.find` method finds objects within the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
@assembly = @part.assemblies.find(1)
@@ -2052,7 +2085,7 @@ The `collection.find` method finds objects within the collection. It uses the sa
##### `collection.where(...)`
-The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection.
+The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed.
```ruby
@new_assemblies = @part.assemblies.where("created_at > ?", 2.days.ago)
@@ -2062,7 +2095,7 @@ The `collection.where` method finds objects within the collection based on the c
The `collection.exists?` method checks whether an object meeting the supplied
conditions exists in the collection. It uses the same syntax and options as
-[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
+[`ActiveRecord::Base.exists?`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
##### `collection.build(attributes = {})`
@@ -2084,6 +2117,14 @@ The `collection.create` method returns a new object of the associated type. This
Does the same as `collection.create`, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@assemblies = @part.assemblies.reload
+```
+
#### Options for `has_and_belongs_to_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
@@ -2121,7 +2162,7 @@ end
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -2309,6 +2350,17 @@ end
If a `before_add` callback throws an exception, the object does not get added to the collection. Similarly, if a `before_remove` callback throws an exception, the object does not get removed from the collection.
+NOTE: These callbacks are called only when the associated objects are added or removed through the association collection:
+
+```ruby
+# Triggers `before_add` callback
+author.books << book
+author.books = [book, book2]
+
+# Does not trigger the `before_add` callback
+book.update(author_id: 1)
+```
+
### Association Extensions
You're not limited to the functionality that Rails automatically builds into association proxy objects. You can also extend these objects through anonymous modules, adding new finders, creators, or other methods. For example:
@@ -2351,7 +2403,7 @@ Single Table Inheritance
------------------------
Sometimes, you may want to share fields and behavior between different models.
-Let's say we have Car, Motorcycle and Bicycle models. We will want to share
+Let's say we have Car, Motorcycle, and Bicycle models. We will want to share
the `color` and `price` fields and some methods for all of them, but having some
specific behavior for each, and separated controllers too.