4.8. has_many – 1:n Association

In order to explain has_many, let's create a bookshelf application. In this database, there is a model with books and a model with authors. As a book can have multiple authors, we need a 1:n association (one-to-many association) to represent it.

Buy the new Rails 5.1 version of this book.

Associations are also sometimes referred to as relations or relationships.
First, we create a Rails application:
$ rails new bookshelf
  [...]
$ cd bookshelf 
$ 
Now we create the model for the books:
$ rails generate model book title
  [...]
$
And finally, we create the database table for the authors. In this, we need an assignment field to the books table. This foreign key is always set by default as name of the referenced object (here: book) with an attached _id:
$ rails generate model author book_id:integer first_name last_name
  [...]
$
Then execute a rake db:migrate so that the database tables are actually created:
$ rake db:migrate
  [...]
$
Let's have a look at this on the console:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book
=> Book(id: integer, title: string, created_at: datetime, updated_at: datetime)
>> Author
=> Author(id: integer, book_id: integer, first_name: string, last_name: string, created_at: datetime, updated_at: datetime)
>> exit
$
The two database tables are set up and can be used with ActiveRecord. But ActiveRecord does not yet know anything of the 1:n relation between them. But this can be done in two small steps.
First we add the line has_many :authors in the app/models/book.rb file to set the 1:n relationship:
class Book < ActiveRecord::Base
  has_many :authors
end
Than we add belongs_to :book in the app/models/author.rb file to get the other way around configured (this is not always needed but often comes in handy):
class Author < ActiveRecord::Base
  belongs_to :book
end
These two simple definitions form the basis for a good deal of ActiveRecord magic. It will generate a bunch of cool new methods for us to link both models.

Creating Records

In this example, we want to save a record for the book "Homo faber" by Max Frisch.

Manually

We drop the database with rake db:reset
$ rake db:reset
  [...]
$  
Befor using the magic we'll insert a book with an author manually. For that we have to use the book's id in the book_id attribute to create the author.
$ rails console
Loading development environment (Rails 4.0.0)
>> book = Book.create(title: 'Homo faber')
   (0.1ms)  begin transaction
  SQL (2.3ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 11:58:17 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 11:58:17 UTC +00:00]]
   (3.0ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 11:58:17", updated_at: "2013-07-16 11:58:17">
>> author = Author.create(book_id: book.id, first_name: 'Max', last_name: 'Frisch')
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 11:58:21 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 16 Jul 2013 11:58:21 UTC +00:00]]
   (3.1ms)  commit transaction
=> #<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 11:58:21", updated_at: "2013-07-16 11:58:21">
>> exit
$
Entering the book_id manually in this way is of course not very practical and susceptible to errors. That's why there is the method the section called “create”.

create

Now we try doing the same as in the section called “Manually”, but this time we use a bit of ActiveRecord magic. We can use the method create of authors to add new authors to each Book object. These automatically get the correct book_id:
$ rake db:reset
  [...]
$ rails console
Loading development environment (Rails 4.0.0)
>> book = Book.create(title: 'Homo faber')
   (0.1ms)  begin transaction
  SQL (2.2ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 12:01:01 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 12:01:01 UTC +00:00]]
   (3.1ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 12:01:01", updated_at: "2013-07-16 12:01:01">
>> author = book.authors.create(first_name: 'Max', last_name: 'Frisch')
   (0.0ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 12:01:23 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 16 Jul 2013 12:01:23 UTC +00:00]]
   (0.8ms)  commit transaction
=> #<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 12:01:23", updated_at: "2013-07-16 12:01:23">
>> exit
$
You could also place the authors.create() directly behind the Book.create():
$ rake db:reset
  [...]
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.create(title: 'Homo faber').authors.create(first_name: 'Max', last_name: 'Frisch')
   (0.1ms)  begin transaction
  SQL (2.2ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 12:02:36 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 12:02:36 UTC +00:00]]
   (2.6ms)  commit transaction
   (0.0ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 12:02:36 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 16 Jul 2013 12:02:36 UTC +00:00]]
   (0.9ms)  commit transaction
=> #<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 12:02:36", updated_at: "2013-07-16 12:02:36">
>> exit
$
As create also accepts an array of hashes as an alternative to a single hash, you can also create multiple authors for a book in one go:
$ rake db:reset
  [...]
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.create(title: 'Example').authors.create([{last_name: 'A'}, {last_name: 'B'}])
   (0.1ms)  begin transaction
  SQL (2.1ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00], ["title", "Example"], ["updated_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00]]
   (3.0ms)  commit transaction
   (0.0ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "last_name", "updated_at") VALUES (?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00], ["last_name", "A"], ["updated_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00]]
   (0.8ms)  commit transaction
   (0.0ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "authors" ("book_id", "created_at", "last_name", "updated_at") VALUES (?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00], ["last_name", "B"], ["updated_at", Tue, 16 Jul 2013 12:03:30 UTC +00:00]]
   (0.8ms)  commit transaction
=> [#<Author id: 1, book_id: 1, first_name: nil, last_name: "A", created_at: "2013-07-16 12:03:30", updated_at: "2013-07-16 12:03:30">, #<Author id: 2, book_id: 1, first_name: nil, last_name: "B", created_at: "2013-07-16 12:03:30", updated_at: "2013-07-16 12:03:30">]
>> exit
$ 

build

The method build resembles create. But the record is not saved. This only happens after a save:
$ rake db:reset
  [...]
$ rails console
Loading development environment (Rails 4.0.0)
>> book = Book.create(title: 'Homo faber')
   (0.1ms)  begin transaction
  SQL (24.5ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Sun, 18 Nov 2012 11:35:35 UTC +00:00], ["title", "Homo faber"], ["updated_at", Sun, 18 Nov 2012 11:35:35 UTC +00:00]]
   (3.0ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2012-11-18 11:35:35", updated_at: "2012-11-18 11:35:35">
>> author = book.authors.build(first_name: 'Max', last_name: 'Frisch')
=> #<Author id: nil, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: nil, updated_at: nil>
>> author.new_record?
=> true
>> author.save
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 1], ["created_at", Sun, 18 Nov 2012 11:36:12 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Sun, 18 Nov 2012 11:36:12 UTC +00:00]]
   (2.5ms)  commit transaction
=> true
>> author.new_record?
=> false
>> exit
$ 

Buy the new Rails 5.1 version of this book.

When using create and build, you of course have to observe logical dependencies, otherwise there will be an error. For example, you cannot chain two build methods. Example:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.build(title: 'Example').authors.build(last_name: 'A')
NoMethodError: undefined method `build' for #<Class:0x007fcc6ce71ab8>
[...]
>> exit
$ 

Accessing Records

First we need example data. Please populate the file db/seeds.rb with the following content:
Book.create(title: 'Homo faber').authors.create(first_name: 'Max', last_name: 'Frisch')
Book.create(title: 'Der Besuch der alten Dame').authors.create(first_name: 'Friedrich', last_name: 'Dürrenmatt')
Book.create(title: 'Julius Shulman: The Last Decade').authors.create([
  {first_name: 'Thomas', last_name: 'Schirmbock'},
  {first_name: 'Julius', last_name: 'Shulman'},
  {first_name: 'Jürgen', last_name: 'Nogai'}
  ])
Book.create(title: 'Julius Shulman: Palm Springs').authors.create([
  {first_name: 'Michael', last_name: 'Stern'},
  {first_name: 'Alan', last_name: 'Hess'}
  ])
Book.create(title: 'Photographing Architecture and Interiors').authors.create([
  {first_name: 'Julius', last_name: 'Shulman'},
  {first_name: 'Richard', last_name: 'Neutra'}
  ])
Book.create(title: 'Der Zauberberg').authors.create(first_name: 'Thomas', last_name: 'Mann')
Book.create(title: 'In einer Familie').authors.create(first_name: 'Heinrich', last_name: 'Mann')
Now drop the database and refill it with the db/seeds.rb:
$ rake db:reset
  [...]
$
The convenient feature of the 1:n assignment in ActiveRecord is the particularly easy access to the n instances. Let's look at the first record:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.first
  Book Load (0.1ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT 1
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 12:05:49", updated_at: "2013-07-16 12:05:49">
>> Book.first.authors
  Book Load (0.3ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT 1
  Author Load (1.4ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">]>
>> 
Isn't that cool?! You can access the records simply via the plural form of the n model. The result is returned as array. Hm, maybe it also works the other way round?
>> Author.first.book
  Author Load (0.4ms)  SELECT "authors".* FROM "authors" ORDER BY "authors"."id" ASC LIMIT 1
  Book Load (0.2ms)  SELECT "books".* FROM "books" WHERE "books"."id" = ? ORDER BY "books"."id" ASC LIMIT 1  [["id", 1]]
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 12:05:49", updated_at: "2013-07-16 12:05:49">
>> exit
$
Bingo! Accessing the associated Book class is also very easy. And as it's only a single record (belongs_to), the singular form is used in this case.

Buy the new Rails 5.1 version of this book.

If there was no author for this book, the result would be an empty array. If no book is associated with an author, then ActiveRecord outputs the value nil as Book.

Searching For Records

Before we can start searching, we again need defined example data. Please fill the file db/seeds.rb with the following content (its the same as we used in the section called “Accessing Records”):
Book.create(title: 'Homo faber').authors.create(first_name: 'Max', last_name: 'Frisch')
Book.create(title: 'Der Besuch der alten Dame').authors.create(first_name: 'Friedrich', last_name: 'Dürrenmatt')
Book.create(title: 'Julius Shulman: The Last Decade').authors.create([
  {first_name: 'Thomas', last_name: 'Schirmbock'},
  {first_name: 'Julius', last_name: 'Shulman'},
  {first_name: 'Jürgen', last_name: 'Nogai'}
  ])
Book.create(title: 'Julius Shulman: Palm Springs').authors.create([
  {first_name: 'Michael', last_name: 'Stern'},
  {first_name: 'Alan', last_name: 'Hess'}
  ])
Book.create(title: 'Photographing Architecture and Interiors').authors.create([
  {first_name: 'Julius', last_name: 'Shulman'},
  {first_name: 'Richard', last_name: 'Neutra'}
  ])
Book.create(title: 'Der Zauberberg').authors.create(first_name: 'Thomas', last_name: 'Mann')
Book.create(title: 'In einer Familie').authors.create(first_name: 'Heinrich', last_name: 'Mann')
Now drop the database and refill it with the db/seeds.rb:
$ rake db:reset
  [...]
$
And off we go. First we check how many books are in the database:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.count
   (0.1ms)  SELECT COUNT(*) FROM "books"
=> 7
>>
And how many authors?
>> Author.count
   (0.1ms)  SELECT COUNT(*) FROM "authors" 
=> 11
>> exit
$ 

joins

To find all books that have at least one author with the surname 'Mann' we use a join.
[10]
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.joins(:authors).where(:authors => {last_name: 'Mann'})
  Book Load (0.5ms)  SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."book_id" = "books"."id" WHERE "authors"."last_name" = 'Mann'
=> #<ActiveRecord::Relation [#<Book id: 6, title: "Der Zauberberg", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">, #<Book id: 7, title: "In einer Familie", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">]>
>>
The database contains two books with the author 'Mann'. In the SQL, you can see that the method joins executes an INNER JOIN.
Of course, we can also do it the other way round. We could search for the author of the book 'Homo faber':
>> Author.joins(:book).where(:books => {title: 'Homo faber'})
  Author Load (0.3ms)  SELECT "authors".* FROM "authors" INNER JOIN "books" ON "books"."id" = "authors"."book_id" WHERE "books"."title" = 'Homo faber'
=> #<ActiveRecord::Relation [#<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">]>
>> exit
$

includes

includes is very similar to the method joins (see the section called “joins”). Again, you can use it to search within a 1:n association. Let's once more search for all books with an author whose surname is 'Mann':
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.includes(:authors).where(:authors => {last_name: 'Mann'})
  SQL (0.3ms)  SELECT "books"."id" AS t0_r0, "books"."title" AS t0_r1, "books"."created_at" AS t0_r2, "books"."updated_at" AS t0_r3, "authors"."id" AS t1_r0, "authors"."book_id" AS t1_r1, "authors"."first_name" AS t1_r2, "authors"."last_name" AS t1_r3, "authors"."created_at" AS t1_r4, "authors"."updated_at" AS t1_r5 FROM "books" LEFT OUTER JOIN "authors" ON "authors"."book_id" = "books"."id" WHERE "authors"."last_name" = 'Mann'
=> #<ActiveRecord::Relation [#<Book id: 6, title: "Der Zauberberg", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">, #<Book id: 7, title: "In einer Familie", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">]>
>> exit
$ 
In the console output, you can see that the SQL code is different from the joins query.
joins only reads in the Book records and includes also reads the associated Authors. As you can see even in our little example, this obviously takes longer (0.2 ms vs. 0.3 ms).

join vs. includes

Why would you want to use includes at all? Well, if you already know before the query that you will later need all author data, then it makes sense to use includes, because then you only need one database query. That is a lot faster than starting a seperate query for each n.
In that case, would it not be better to always work with includes? No, it depends on the specific case. When you are using includes, a lot more data is transported initially. This has to be cached and processed by ActiveRecord, which takes longer and requires more resources.

delete and destroy

With the methods destroy, destroy_all, delete and delete_all you can delete records, as described in Section 4.12, “Delete/Destroy a Record”. In the context of has_many, this means that you can delete the Author records associated with a Book in one go:
$ rails console
Loading development environment (Rails 4.0.0)
>> book = Book.where(title: 'Julius Shulman: The Last Decade').first
  Book Load (0.1ms)  SELECT "books".* FROM "books" WHERE "books"."title" = 'Julius Shulman: The Last Decade' ORDER BY "books"."id" ASC LIMIT 1
=> #<Book id: 3, title: "Julius Shulman: The Last Decade", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">
>> book.authors.count
   (1.7ms)  SELECT COUNT(*) FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 3]]
=> 3
>> book.authors.destroy_all
  Author Load (0.4ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 3]]
   (0.1ms)  begin transaction
  SQL (0.5ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 3]]
  SQL (0.1ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 4]]
  SQL (0.0ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 5]]
   (2.4ms)  commit transaction
=> [#<Author id: 3, book_id: 3, first_name: "Thomas", last_name: "Schirmbock", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">, #<Author id: 4, book_id: 3, first_name: "Julius", last_name: "Shulman", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">, #<Author id: 5, book_id: 3, first_name: "Jürgen", last_name: "Nogai", created_at: "2013-07-16 12:05:50", updated_at: "2013-07-16 12:05:50">]
>> book.authors.count
   (0.2ms)  SELECT COUNT(*) FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 3]]
=> 0
>> exit
$

Options

I can't comment on all possible options at this point. But I'd like to show you the most often used ones. For all others, please refer to the Ruby on Rails documentation that you can find on the Internet at http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html.

belongs_to

The most important option for belongs_to is.
touch: true
It automatically sets the field updated_at of the entry in the table Book to the current time when an Author is edited. In the app/models/author.rb, it would look like this:
class Author < ActiveRecord::Base
  belongs_to :book, touch: true
end

has_many

The most important options for has_many are.
order: :last_name
If you want to sort the authors by surname, you can do this via the following app/models/book.rb:
class Book < ActiveRecord::Base
  has_many :authors, order: :last_name
end
As an example, let's create a new book with new authors and see how ActiveRecord sorts them:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.create(title: 'Test').authors.create([{last_name: 'Z'}, {last_name: 'A'}]) 
   (0.1ms)  begin transaction
  SQL (23.5ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00], ["title", "Test"], ["updated_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00]]
   (2.6ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 8], ["created_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00], ["first_name", nil], ["last_name", "Z"], ["updated_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00]]
   (0.8ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 8], ["created_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00], ["first_name", nil], ["last_name", "A"], ["updated_at", Sun, 18 Nov 2012 12:04:31 UTC +00:00]]
   (0.8ms)  commit transaction
=> [#<Author id: 12, book_id: 8, first_name: nil, last_name: "Z", created_at: "2012-11-18 12:04:31", updated_at: "2012-11-18 12:04:31">, #<Author id: 13, book_id: 8, first_name: nil, last_name: "A", created_at: "2012-11-18 12:04:31", updated_at: "2012-11-18 12:04:31">]
>> Book.last.authors
  Book Load (0.3ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 8 ORDER BY last_name
=> [#<Author id: 13, book_id: 8, first_name: nil, last_name: "A", created_at: "2012-11-18 12:04:31", updated_at: "2012-11-18 12:04:31">, #<Author id: 12, book_id: 8, first_name: nil, last_name: "Z", created_at: "2012-11-18 12:04:31", updated_at: "2012-11-18 12:04:31">]
>> exit
$ 
And if we want to sort in descending order for a change:
has_many :authors, :order => 'title DESC'
dependent: :destroy
If a book is removed, then it usually makes sense to also automatically remove all authors dependent on this book. This can be done via :dependent => :destroy in the app/models/book.rb:
class Book < ActiveRecord::Base
  has_many :authors, dependent: :destroy
end
In the following example, we destroy the first book in the database table. All authors of this book are also automatically destroyed:
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.first
  Book Load (0.1ms)  SELECT "books".* FROM "books" LIMIT 1
=> #<Book id: 1, title: "Homo faber", created_at: "2012-11-18 11:46:29", updated_at: "2012-11-18 11:46:29">
>> Book.first.authors
  Book Load (0.3ms)  SELECT "books".* FROM "books" LIMIT 1
  Author Load (0.1ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 1
=> [#<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2012-11-18 11:46:29", updated_at: "2012-11-18 11:46:29">]
>> Book.first.destroy
  Book Load (0.3ms)  SELECT "books".* FROM "books" LIMIT 1
   (0.1ms)  begin transaction
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 1
  SQL (4.8ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 1]]
  SQL (0.1ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 1]]
   (3.0ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2012-11-18 11:46:29", updated_at: "2012-11-18 11:46:29">
>> Author.exists?(1)
  Author Exists (0.2ms)  SELECT 1 AS one FROM "authors" WHERE "authors"."id" = 1 LIMIT 1
=> false
>> exit
$

Buy the new Rails 5.1 version of this book.

Please always remember the difference between the methods destroy (see the section called “destroy”) and delete (see the section called “delete”). This association only works with the method destroy.
has_many .., through: ..
Here I need to elaborate a bit: you will probably have noticed that in our book-author example we have sometimes been entering authors several times in the authors table. Normally, you would of course not do this. It would be better to enter each author only once in the authors table and take care of the association with the books via an intermediary table. For this purpose, there is has_many , through: => .
This kind of association is called Many-to-Many (n:n) and we'll discuss it in detail in Section 4.9, “Many-to-Many – n:n Association”.


[10] If you are interested in the theoretical background on joins, you will find more information here: http://en.wikipedia.org/wiki/SQL#Queries, http://en.wikipedia.org/wiki/Join_(SQL), http://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators