我有两个型号。它们可以解释如下:
报告有一个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使用别名覆盖
答案 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
允许您指定只能更新现有记录。一个 新记录只能在以下时创建 没有现有记录。这个 选项仅适用于一对一 关联并被忽略 收集协会。这个选项 默认情况下是关闭的。
希望这有帮助,无论如何,如果你找到更好的解决方案,请告诉我。