【问题标题】:ActiveRecord association: create new association or reference existing if associated attributes matchActiveRecord 关联:如果关联的属性匹配,则创建新的关联或引用现有的关联
【发布时间】:2010-09-14 15:37:56
【问题描述】:

我在下面有两个模型。它们可以解释如下:

报告有一个report_detail(确定开始/结束月份)。许多报告可以具有相同的报告详细信息,但没有两个报告详细信息可以相同。

class Report < ActiveRecord::Base
  # attr: name :: String
  # attr: report_detail_id :: Integer
  belongs_to :report_detail
  accepts_nested_attributes_for :report_detail
end

class ReportDetail < ActiveRecord::Base
  # attr: duration :: Integer
  # attr: starting_month :: Integer
  # attr: offset :: Integer
end

我对 ReportDetail 上的索引有一个唯一约束 [:duration, :starting_month, :offset]

我想要完成的是:如果一个新报告的 ReportDetail 具有唯一的组合 attrs (:duration, :starting_month, :offset),则创建新的 ReportDetail 并正常保存。如果报表有一个 ReportDetail,而现有 ReportDetail 具有相同的属性,请将报表的详细信息与此 ReportDetail 相关联并保存报表。

我通过使用ReportDetail.find_or_create_by...report_detail= 上的setter 设置别名来实现这一点,但这很丑(而且它还通过使用detail 属性实例化新报告来创建不必要的ReportDetail 条目,并且由于某种原因我不能使用.find_or_initialize_by... 使保存正常工作)。我还在 ReportDetail 上尝试了 before_save 来表示,如果我匹配其他内容,请将 self 设置为其他内容。显然你不能这样设置自己。

有没有想过解决这个问题的最佳方法?

请参阅 this gist 以了解我当前使用别名覆盖的设置器

【问题讨论】:

  • 你能贴出别名设置器的代码吗?也许作为一个要点并将其链接在这里。听起来这是在正确的轨道上,但可能有一两个错误。
  • 已添加...此代码中没有错误,一切正常,但我正在寻找更好的解决方案。我最大的不满是,当您使用上面的要点进行初始化时,它实际上会创建一个 ReporDetail。我也无法让它与 find_or_initialize_by 一起正常工作,因为关联的保存是在此之前发生的,所以它最终不会在第一次运行时保存关联

标签: ruby-on-rails activerecord associations unique-index


【解决方案1】:

今天刚遇到这个问题,我的解决方案基于object_attributes= 方法,该方法由accepts_nested_attributes_for 提供,我认为这会减少混乱而不是覆盖标准关联设置器方法。潜入 Rails 源代码以找到此解决方案,这里是 github link。代码:

class Report < ActiveRecord::Base
  # attr: name :: String
  # attr: report_detail_id :: Integer
  belongs_to :report_detail
  accepts_nested_attributes_for :report_detail

  def report_detail_attributes=(attributes)
    report_detail = ReportDetail.find_or_create_by_duration_and_display_duration_and_starting_month_and_period_offset(attrs[:duration],attrs[:display_duration],attrs[:starting_month],attrs[:period_offset])
    attributes[:id]=report_detail.id
    assign_nested_attributes_for_one_to_one_association(:report_detail, attributes)
  end
end

一些解释,如果你提供了一个id,会被认为是更新,所以不再创建新的对象。我也知道这种方法需要一个查询,但我现在找不到更好的解决方案。

另外,您似乎在报告和报告详细信息之间有 has_one 关联,如果您是这种情况,您可以试试这个:

   class Report < ActiveRecord::Base
      # attr: name :: String
      # attr: report_detail_id :: Integer
      has_one :report_detail
      accepts_nested_attributes_for :report_detail, :update_only=>true
    end

根据documentation 这应该适合你。 来自 rails3 文档:

:update_only

允许您指定只能更新现有记录。一种 新记录只能在以下情况下创建 没有现有记录。这 选项仅适用于一对一 关联并被忽略 收藏协会。这个选项 默认关闭。

【讨论】:

  • 很抱歉没有回答/接受,我已经切换到别的东西,没有机会测试,我会更新,谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
相关资源
最近更新 更多