has_many和:after_add /:before_add =>回调<<和创建方法

Ami*_*tel 4 activerecord ruby-on-rails ruby-on-rails-3

我正在阅读Rails 3 Way的书,并感到困惑:

:after_add => callback 通过<<方法将记录添加到集合中后调用。不是由集合的create方法触发的

据我了解book.chapters.create(title:'第一章')不会调用before_add回调,但实际上是在调用。

class Book < ActiveRecord::Base
  attr_accessible :title
  has_many :chapters, :before_add => :add_chapter

  private
    def add_chapter(chapter)
      logger.error('chapter added to book')
    end
end

class Chapter < ActiveRecord::Base
  belongs_to :book
  attr_accessible :title
end
Run Code Online (Sandbox Code Playgroud)

在控制台中(最小)

 > b = Book.first
  Book Load (0.1ms)  SELECT "books".* FROM "books" LIMIT 1
 > b.chapters.create(title: 'Last Chapter')
  begin transaction
chapter added to book
  INSERT INTO "chapters" ....
  commit transaction
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到after_add为调用了回调create

我误会了吗?

编辑

b.chapters.new(title: 'New Chapter')
b.chapters.build(title: 'New Chapter')
Run Code Online (Sandbox Code Playgroud)

也调用回调

The*_*ter 5

将项目添加到集合时会触发before_addafter_add回调。与是否将记录保存到数据库无关。(对于has_and_belongs_to_manyhas_many :through关系,这里要略作例外,将其添加到集合中后,ActiveRecord会立即将​​其反映到数据库中)。

将新记录添加到集合后,将触发回调。在将元素添加到集合之前,将调用before_add,之后将调用after_add。

下面的示例可以使您更好地理解。

# id: integer
class Author < ActiveRecord::Base
  has_many :books, before_add: :bef, after_add: aft

  def bef
    puts "Before adding, author ##{id} has #{books.size} books"
  end

  def aft
    puts "After adding, author ##{id} has #{books.size} books"
  end
end

# id integer
# author_id: integer
class Book < ActiveRecord::Base
  belongs_to :author
  after_save: :saved

  def saved
    puts "The book is now saved!"
  end
end

> book = Book.new

> author = Author.first

> author.books << book
'Before adding, author #1 has 3 books'
'After adding, author #1 has 4 books'

> author.save
'The book is now saved'
Run Code Online (Sandbox Code Playgroud)

  • 这使该功能变得毫无价值。使用外键直接添加记录时不会触发回调。 (6认同)
  • @maletor 我可以想到这个功能的许多可能的用例。特别是由于 `has_many :through`/habtm 关系中赋值操作的破坏性:除了直接与参考模型交互(这会破坏 :through 的目的)之外,这是您可以 1. 跟踪更改和如果需要,则回滚,或者 2. 访问记录并在将其添加到集合中/从集合中删除之前抛出中止。特别是由于双方都没有进行验证,这比您想象的更重要。 (2认同)