如何获取关联记录的先前值?
How to fetch previous values of associated records?
在处理多对多关系时,我需要维护一个记录更改值的日志文件。
使用 before_save 和 after_save 回调对主 (has_many) 模型本身工作正常,但在 before_save 回调中关联的 (belongs_to) 记录似乎是已经更新了!
在调用 'before_save' 回调之前看到部分数据已经更新似乎很奇怪。
此外,在关联模型中使用回调显示没有执行 before_destroy。只有 before_save 被调用并显示新值。
我还尝试了 :prepend => :true 选项,但没有给出其他结果。
在主 (has_many) 模型中实际保存之前打开 SQL 日志记录时,我可以看到 Rails 正在获取关联记录,确定差异并删除多余记录。未调用关联模型的before_destroy。
然后它调用关联模型的 before_save 并插入新模型(如果有)并提交事务。这一切都在主模型 before_save 之前完成。
有谁知道如何在更改之前获取关联记录?
我希望关联模型的 before_destroy 会被调用并让我处理它。
使用 before_update 并且您可以使用 _was
访问旧值
before_update :record_values
def record_values
p "oldvalue: #{self.field_was} Newvalue: #{self.field}"
end
你的问题有点不清楚,但让我举个例子来说明你想做什么。
下面的代码对我来说很好用:
class Person < ApplicationRecord
has_many :addresses
validates_presence_of :name
before_save { puts "before_save of person - changes: #{changes}" }
before_destroy { puts "before_destroy of person with id: #{id}" }
end
class Address < ApplicationRecord
belongs_to :person, required: true
validates_presence_of :name
before_save { puts "before_save of address - changes: #{changes}" }
before_destroy { puts "before_destroy of address with id: #{id}" }
end
这会在交互时产生以下输出:
person = Person.create(name: 'Johan Wentholt')
# before_save of person - changes: {"name" =>[nil, "Johan Wentholt"]}
#=> #<Person id: 2, name: "Johan Wentholt", created_at: "2017-10-25 15:04:27", updated_at: "2017-10-25 15:04:27">
person.addresses.create(name: 'Address #1')
# before_save of address - changes: {"person_id"=>[nil, 2], "name" =>[nil, "Address #1"]}
#=> #<Address id: 7, person_id: 2, name: "Address #1", created_at: "2017-10-25 15:06:38", updated_at: "2017-10-25 15:06:38">
person.addresses.last.update(name: 'Address without typo')
# before_save of address - changes: {"name"=>["Address #1", "Address without typo"]}
#=> true
person.update(name: 'Kaasboer')
# before_save of person - changes: {"name"=>["Johan Wentholt", "Kaasboer"]}
#=> true
person.addresses.last.destroy
# before_destroy of address with id: 7
#=> #<Address id: 7, person_id: 2, name: "Address without typo", created_at: "2017-10-25 15:06:38", updated_at: "2017-10-25 15:08:51">
person.destroy
# before_destroy of person with id: 2
#=> #<Person id: 2, name: "Kaasboer", created_at: "2017-10-25 15:04:27", updated_at: "2017-10-25 15:10:46">
如您所见,这会记录所有更改。就像我说的,这个问题有点不清楚,但我希望这对你有进一步的帮助。
请记住,某些 Rails 方法不会触发回调。例如:delete、update_all、update_column 等等。
有关更改的更多信息,请查看:ActiveModel::Dirty
为清楚起见,让我们提供一些扩展信息:
class Book < ActiveRecord::Base
unloadable
has_many :titles, dependent: :destroy
has_many :authors, :through => :titles
accepts_nested_attributes_for :authors
before_save :pre_save
after_save :post_save
before_destroy :pre_delete
def pre_save
@nr = self.new_record?
end
def pre_save
changed_values = []
if @nr
changed_values.push "New record created"
else
self.changes.each do |field, cvs|
changes.push("#{field} : #{cvs[0]} => #{cvs[1]}")
end
end
if changes.length > 0
BookLog.create(:book_id => self.id, :changed_values => changes.join(', '))
end
end
def pre_delete
BookLog.create(:book_id => self.id, :changed_values => "Deleted: #{self.name}")
end
end
class Title < ActiveRecord::Base
unloadable
belongs_to :book
belongs_to :author
end
class Author < ActiveRecord::Base
unloadable
has_many :titles, dependent: :destroy
has_many :books, :through => :titles
accepts_nested_attributes_for :books
end
class BooksController < ApplicationController
def edit
book = Book.find(params[:book][:id])
book.name = .....
===> Here the old values are still available <====
book.author_ids = params[:book][:author_ids]
===> Now the new values are written to the database! <====
book.save!
end
end
Book 记录的更改已完美记录。
但是无法获取 author_ids 的已更改关联值。
标题中的 before_destroy 回调未被调用,after_save 被调用。
我在将新 author_ids 分配给已编辑记录之前通过启用 SQL 日志记录来检查这一点。我可以看到 Rails 确定了现有关联值和新关联值之间的差异,删除了标题 table 的多余部分并插入了额外的(如果有的话)
我通过比较旧值和新值将 Titles 中更改的日志记录移至 Books 控制器解决了这个问题:
o_authors = book.author_ids
n_authors = params[:book][:author_ids].collect {|c| c.to_i}
diff = o_authors - n_authors | n_authors - o_authors
if !diff.empty?
changed_values = []
(o_authors - n_authors).each do |d|
changed_values.push("Removed Author: #{Author.find(d).name}")
end
(n_authors - o_authors).each do |d|
changed_values.push("Added Author: #{Author.find(d).name}")
end
BookLog.create(:book_id => book.id, :changed_values => changed_values)
end
book.author_ids = params[:book][:author_ids]
book.save!
正如我所说,它有效,但恕我直言,它并没有反映 Rails 做事的方式。我本希望以与任何其他 Book 属性相同的方式获得前一个 author_ids。
在处理多对多关系时,我需要维护一个记录更改值的日志文件。 使用 before_save 和 after_save 回调对主 (has_many) 模型本身工作正常,但在 before_save 回调中关联的 (belongs_to) 记录似乎是已经更新了! 在调用 'before_save' 回调之前看到部分数据已经更新似乎很奇怪。
此外,在关联模型中使用回调显示没有执行 before_destroy。只有 before_save 被调用并显示新值。 我还尝试了 :prepend => :true 选项,但没有给出其他结果。
在主 (has_many) 模型中实际保存之前打开 SQL 日志记录时,我可以看到 Rails 正在获取关联记录,确定差异并删除多余记录。未调用关联模型的before_destroy。 然后它调用关联模型的 before_save 并插入新模型(如果有)并提交事务。这一切都在主模型 before_save 之前完成。
有谁知道如何在更改之前获取关联记录? 我希望关联模型的 before_destroy 会被调用并让我处理它。
使用 before_update 并且您可以使用 _was
before_update :record_values
def record_values
p "oldvalue: #{self.field_was} Newvalue: #{self.field}"
end
你的问题有点不清楚,但让我举个例子来说明你想做什么。
下面的代码对我来说很好用:
class Person < ApplicationRecord
has_many :addresses
validates_presence_of :name
before_save { puts "before_save of person - changes: #{changes}" }
before_destroy { puts "before_destroy of person with id: #{id}" }
end
class Address < ApplicationRecord
belongs_to :person, required: true
validates_presence_of :name
before_save { puts "before_save of address - changes: #{changes}" }
before_destroy { puts "before_destroy of address with id: #{id}" }
end
这会在交互时产生以下输出:
person = Person.create(name: 'Johan Wentholt')
# before_save of person - changes: {"name" =>[nil, "Johan Wentholt"]}
#=> #<Person id: 2, name: "Johan Wentholt", created_at: "2017-10-25 15:04:27", updated_at: "2017-10-25 15:04:27">
person.addresses.create(name: 'Address #1')
# before_save of address - changes: {"person_id"=>[nil, 2], "name" =>[nil, "Address #1"]}
#=> #<Address id: 7, person_id: 2, name: "Address #1", created_at: "2017-10-25 15:06:38", updated_at: "2017-10-25 15:06:38">
person.addresses.last.update(name: 'Address without typo')
# before_save of address - changes: {"name"=>["Address #1", "Address without typo"]}
#=> true
person.update(name: 'Kaasboer')
# before_save of person - changes: {"name"=>["Johan Wentholt", "Kaasboer"]}
#=> true
person.addresses.last.destroy
# before_destroy of address with id: 7
#=> #<Address id: 7, person_id: 2, name: "Address without typo", created_at: "2017-10-25 15:06:38", updated_at: "2017-10-25 15:08:51">
person.destroy
# before_destroy of person with id: 2
#=> #<Person id: 2, name: "Kaasboer", created_at: "2017-10-25 15:04:27", updated_at: "2017-10-25 15:10:46">
如您所见,这会记录所有更改。就像我说的,这个问题有点不清楚,但我希望这对你有进一步的帮助。
请记住,某些 Rails 方法不会触发回调。例如:delete、update_all、update_column 等等。
有关更改的更多信息,请查看:ActiveModel::Dirty
为清楚起见,让我们提供一些扩展信息:
class Book < ActiveRecord::Base
unloadable
has_many :titles, dependent: :destroy
has_many :authors, :through => :titles
accepts_nested_attributes_for :authors
before_save :pre_save
after_save :post_save
before_destroy :pre_delete
def pre_save
@nr = self.new_record?
end
def pre_save
changed_values = []
if @nr
changed_values.push "New record created"
else
self.changes.each do |field, cvs|
changes.push("#{field} : #{cvs[0]} => #{cvs[1]}")
end
end
if changes.length > 0
BookLog.create(:book_id => self.id, :changed_values => changes.join(', '))
end
end
def pre_delete
BookLog.create(:book_id => self.id, :changed_values => "Deleted: #{self.name}")
end
end
class Title < ActiveRecord::Base
unloadable
belongs_to :book
belongs_to :author
end
class Author < ActiveRecord::Base
unloadable
has_many :titles, dependent: :destroy
has_many :books, :through => :titles
accepts_nested_attributes_for :books
end
class BooksController < ApplicationController
def edit
book = Book.find(params[:book][:id])
book.name = .....
===> Here the old values are still available <====
book.author_ids = params[:book][:author_ids]
===> Now the new values are written to the database! <====
book.save!
end
end
Book 记录的更改已完美记录。 但是无法获取 author_ids 的已更改关联值。 标题中的 before_destroy 回调未被调用,after_save 被调用。
我在将新 author_ids 分配给已编辑记录之前通过启用 SQL 日志记录来检查这一点。我可以看到 Rails 确定了现有关联值和新关联值之间的差异,删除了标题 table 的多余部分并插入了额外的(如果有的话)
我通过比较旧值和新值将 Titles 中更改的日志记录移至 Books 控制器解决了这个问题:
o_authors = book.author_ids
n_authors = params[:book][:author_ids].collect {|c| c.to_i}
diff = o_authors - n_authors | n_authors - o_authors
if !diff.empty?
changed_values = []
(o_authors - n_authors).each do |d|
changed_values.push("Removed Author: #{Author.find(d).name}")
end
(n_authors - o_authors).each do |d|
changed_values.push("Added Author: #{Author.find(d).name}")
end
BookLog.create(:book_id => book.id, :changed_values => changed_values)
end
book.author_ids = params[:book][:author_ids]
book.save!
正如我所说,它有效,但恕我直言,它并没有反映 Rails 做事的方式。我本希望以与任何其他 Book 属性相同的方式获得前一个 author_ids。