ActiveRecord关联:如果关联的属性匹配,则创建新的关联或引用

时间:2010-09-14 15:37:57

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

我有两个型号。它们可以解释如下:

报告有一个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

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

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

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

有没有想过最好的方法呢?

请参阅this gist了解我当前的setter使用别名覆盖

1 个答案:

答案 0 :(得分:1)

今天刚遇到这个问题,我的解决方案基于object_attributes=方法,该方法由accepts_nested_attributes_for提供,我认为这样可以减少混乱而不是覆盖标准关联setter方法。潜入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

     

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

希望这有帮助,无论如何,如果你找到更好的解决方案,请告诉我。