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-RELATION
和before_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
谢谢。