我有这两个班。
class Article < ActiveRecord::Base
attr_accessible :body, :issue, :name, :page, :image, :video, :brand_ids
has_many :publications
has_many :docs, :through => :publications
end
class Doc < ActiveRecord::Base
attr_accessible :issue_id, :cover_id, :message, :article_ids, :user_id, :created_at, :updated_at, :issue_code, :title, :template_id
has_many :publications, dependent: :destroy
has_many :articles, :through => :publications, :order => 'publications.position'
has_many :edits, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: false
end
如何在更新@doc.articles
后编写条件语句以查看@doc
是否已更改?
if @doc.articles.changed?
...
end
上面给我一个错误。我找不到正确的语法。
答案 0 :(得分:19)
你必须检查每一个。 .changed?
仅适用于单个记录。如果您需要检查整个关联以进行至少一次更改,则可以执行此类操作:
if @doc.articles.find_index {|a| a.changed?} then...
或者您可以使用Enumerable#any?
:
if @doc.articles.any? {|a| a.changed?} then...
答案 1 :(得分:7)
有点晚了,但是对于正在寻找类似解决方案的其他人来说,你可以通过这种方式检测关系中的变化(也是has_and_belongs_to_many):
class Article < ActiveRecord::Base
attr_accessible :body, :issue, :name, :page, :image, :video, :brand_ids
has_many :publications
has_many :docs, :through => :publications
end
class Doc < ActiveRecord::Base
attr_accessible :issue_id, :cover_id, :message, :article_ids, :user_id, :created_at, :updated_at, :issue_code, :title, :template_id
has_many :publications, dependent: :destroy
has_many :articles, :through => :publications, :order => 'publications.position'
has_many :edits, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: false
after_initialize :initialize_article_changes_safe
after_save :change_articles
after_save :initialize_article_changes
before_add_for_articles << ->(method,owner,change) { owner.send(:on_add_article, change) }
before_remove_for_articles << ->(method,owner,change) { owner.send(:on_remove_article, change) }
def articles_changed?
@article_changes[:removed].present? or @article_changes[:added].present?
end
private
def on_add_article(article)
initialize_article_changes_safe
@article_changes[:added] << article.id
end
def on_remove_article(article)
initialize_article_changes_safe
@article_changes[:removed] << article.id
end
def initialize_article_changes
@article_changes = {added: [], removed: []}
end
def initialize_article_changes_safe
@article_changes = {added: [], removed: []} if @article_changes.nil?
end
def unchanged_article_ids
self.article_ids - @article_changes[:added] - @article_changes[:removed]
end
def change_articles
do_stuff if self.articles_changed?
do_stuff_for_added_articles unless @article_changes[:added].nil?
do_stuff_for_removed_articles unless @article_changes[:removed].nil?
end
end
添加或删除关系时会触发两个挂钩before_add_for_NAME-OF-RELATION
和before_remove_for_NAME-OF-RELATION
。触发的函数(您不能通过名称链接函数,您必须通过lambda执行)将添加/删除的关系项的ID添加到@articel_changes
哈希。保存模型后,您可以通过change_articles
函数中的ID来处理对象。之后,@articel_changes
哈希将被清除。
答案 2 :(得分:2)
我发现可枚举#any?有助于以下列方式按ID进行反向排序的中间步骤:
@doc.articles.sort { |a, b| b.id <=> a.id }.any?(&:changed?)
该排序步骤有助于任何?提前返回,而不是循环浏览较旧的文章记录,以查找是否进行了任何更改。
例如在我的情况下,我有一个has_many :event_responders
关系,在向集合中添加新的EventResponder
后,我验证了以上方式:
不使用中间排序
2.2.1 :019 > ep.event_responders.any? { |a| puts a.changes; a.changed? }
{}
{}
{}
{}
{"created_at"=>[Thu, 03 Sep 2015 08:25:59 UTC +00:00, Thu, 03 Sep 2015 08:25:59 UTC +00:00], "updated_at"=>[Thu, 03 Sep 2015 08:25:59 UTC +00:00, Thu, 03 Sep 2015 08:25:59 UTC +00:00]}
=> true
使用中间排序
2.2.1 :020 > ep.event_responders.sort { |a, b| b.id <=> a.id }.any? { |a| puts a.changes; a.changed? }
{"created_at"=>[Thu, 03 Sep 2015 08:25:59 UTC +00:00, Thu, 03 Sep 2015 08:25:59 UTC +00:00], "updated_at"=>[Thu, 03 Sep 2015 08:25:59 UTC +00:00, Thu, 03 Sep 2015 08:25:59 UTC +00:00]}
=> true
感谢。
答案 3 :(得分:2)
使用after_add
和after_remove
关联回调来检查添加/删除,同时使用saved_changes?
来检查现有文章的任何更改。
class Doc < ActiveRecord::Base
has_many :articles, after_add: :set_article_flag, after_remove: :set_article_flag
after_save :do_something_with_changed_articles
private
def set_article_flag
@articles_changed = true
end
def do_something_with_changed_articles
if @articles_changed || articles.any?(&:saved_changes?)
# do something
end
end
end
答案 4 :(得分:0)
以@jangosteve's comment为基础,可以创建一个魔术方法来响应[association] _changed ?,无论其关联名称是什么:
# app/model/application_record.rb
def method_missing(method_name, *arguments, &block)
if method_name.to_s =~ /(.*)_changed?/
association = $1.to_sym
send(association).any?(&:changed?) || send(association).collect(&:id).sort != send(association).pluck(:id).sort
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
if method_name.to_s =~ /(.*)_changed?/
association = $1.to_sym
self.class.reflect_on_association(association).present?
else
super
end
end