Rails:如果has_many关系发生了变化

我有这两个class。

class Article  :publications end class Doc  :publications, :order => 'publications.position' has_many :edits, dependent: :destroy accepts_nested_attributes_for :articles, allow_destroy: false end 

在更新@doc后,如何编写条件语句以查看@doc.articles是否已更改?

 if @doc.articles.changed? ... end 

上面给了我一个错误。 我找不到正确的语法。

你必须检查每一个。 .changed? 仅适用于单个记录。 如果您需要检查整个关联以进行至少一次更改,您可以执行以下操作:

 if @doc.articles.find_index {|a| a.changed?} then... 

或者你可以使用Enumerable#any?

 if @doc.articles.any? {|a| a.changed?} then... 

有点晚了,但是对于正在寻找类似解决方案的其他人,您可以通过以下方式检测关系中的更改(也包括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-RELATIONbefore_remove_for_NAME-OF-RELATION 。 触发的函数(您不能通过名称链接函数,您必须通过lambda执行)将添加/删除的关系项的ID添加到@articel_changes哈希。 保存模型后,您可以通过change_articles函数中的ID来处理对象。 之后, @articel_changes哈希将被清除。

我找到了Enumerable#any? 有助于以下列方式通过id进行反向排序的中间步骤:

 @doc.articles.sort { |a, b| b.id <=> a.id }.any?(&:changed?) 

那个排序步骤有帮助吗? 提前返回,而不是循环查看较旧的文章记录,以查找是否进行了任何更改。

例如,在我的情况下,我有一个has_many :event_responders关系,并在向集合添加新的EventResponder之后,我validation了以上方式:

不使用中间排序

 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 

谢谢。