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.md129
1 files changed, 85 insertions, 44 deletions
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 808aa9d691..a60ce7fb32 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
==========================
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -384,7 +384,7 @@ end
The corresponding migration might look like this:
```ruby
-class CreateSuppliers < ActiveRecord::Migration[5.0]
+class CreateSuppliers < ActiveRecord::Migration[5.2]
def change
create_table :suppliers do |t|
t.string :name
@@ -392,7 +392,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0]
end
create_table :accounts do |t|
- t.integer :supplier_id
+ t.bigint :supplier_id
t.string :account_number
t.timestamps
end
@@ -402,7 +402,7 @@ class CreateSuppliers < ActiveRecord::Migration[5.0]
end
```
-NOTE: Using `t.integer :supplier_id` makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using `t.references :supplier` instead.
+NOTE: Using `t.bigint :supplier_id` makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using `t.references :supplier` instead.
### Choosing Between `has_many :through` and `has_and_belongs_to_many`
@@ -466,11 +466,11 @@ Similarly, you can retrieve `@product.pictures`.
If you have an instance of the `Picture` model, you can get to its parent via `@picture.imageable`. To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface:
```ruby
-class CreatePictures < ActiveRecord::Migration[5.0]
+class CreatePictures < ActiveRecord::Migration[5.2]
def change
create_table :pictures do |t|
t.string :name
- t.integer :imageable_id
+ t.bigint :imageable_id
t.string :imageable_type
t.timestamps
end
@@ -487,7 +487,7 @@ 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
@@ -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
@@ -619,11 +619,11 @@ end
These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key:
```ruby
-class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0]
+class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.2]
def change
create_table :assemblies_parts, id: false do |t|
- t.integer :assembly_id
- t.integer :part_id
+ t.bigint :assembly_id
+ t.bigint :part_id
end
add_index :assemblies_parts, :assembly_id
@@ -868,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
```
@@ -1048,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
```
@@ -1075,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
@@ -1089,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
@@ -1258,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
@@ -1306,6 +1305,21 @@ The `:source` option specifies the source association name for a `has_one :throu
The `:source_type` option specifies the source association type for a `has_one :through` association that proceeds through a polymorphic association.
+```ruby
+class Book < ApplicationRecord
+ has_one :format, polymorphic: true
+ has_one :dust_jacket, through: :format, source: :dust_jacket, source_type: "Hardback"
+end
+
+class Paperback < ApplicationRecord; end
+
+class Hardback < ApplicationRecord
+ has_one :dust_jacket
+end
+
+class DustJacket < ApplicationRecord; end
+```
+
##### `:through`
The `:through` option specifies a join model through which to perform the query. `has_one :through` associations were discussed in detail [earlier in this guide](#the-has-one-through-association).
@@ -1545,7 +1559,7 @@ 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`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
+[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
@available_book = @author.books.find(1)
@@ -1564,7 +1578,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 = {}, ...)`
@@ -1659,10 +1673,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:
@@ -1716,6 +1732,20 @@ The `:source` option specifies the source association name for a `has_many :thro
The `:source_type` option specifies the source association type for a `has_many :through` association that proceeds through a polymorphic association.
+```ruby
+class Author < ApplicationRecord
+ has_many :books
+ has_many :paperbacks, through: :books, source: :format, source_type: "Paperback"
+end
+
+class Book < ApplicationRecord
+ has_one :format, polymorphic: true
+end
+
+class Hardback < ApplicationRecord; end
+class Paperback < ApplicationRecord; end
+```
+
##### `:through`
The `:through` option specifies a join model through which to perform the query. `has_many :through` associations provide a way to implement many-to-many relationships, as discussed [earlier in this guide](#the-has-many-through-association).
@@ -1779,8 +1809,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
```
@@ -1795,27 +1825,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
```
@@ -2076,7 +2106,7 @@ 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`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
+[`ActiveRecord::Base.find`](https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
@assembly = @part.assemblies.find(1)
@@ -2094,7 +2124,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 = {})`
@@ -2349,6 +2379,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: