22

我有这两个班。

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

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

4

5 回答 5

20

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

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

或者您可以使用Enumerable#any?

if @doc.articles.any? {|a| a.changed?} then...
于 2013-05-24T03:21:13.013 回答
13

使用after_addafter_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
于 2020-03-09T13:35:07.167 回答
12

有点晚了,但是对于正在寻找类似解决方案的其他人,您可以通过这种方式检测关系(也有 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哈希将被清除。

于 2016-09-08T15:44:15.633 回答
3

我发现Enumerable#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 

谢谢。

于 2015-09-03T08:45:12.713 回答
0

@jangosteve 的评论的基础上,您可以创建一个响应 [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
于 2020-03-08T12:06:10.213 回答