Ассоциация ActiveRecord: создайте новую ассоциацию или существующую ссылку, если соответствующие атрибуты совпадают - PullRequest
0 голосов
/ 14 сентября 2010

У меня есть две модели ниже.Их можно объяснить следующим образом:

В отчете есть 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,: initial_month,: offset]

Я пытаюсь сделать следующее: если в новом отчете есть ReportDetail с уникальной комбинацией атрибутов (: duration,: initial_month,: offset), создайте новый ReportDetail и сохраните как обычно.Если в отчете есть ReportDetail, при котором существующий ReportDetail имеет те же атрибуты, свяжите детали отчета с этим ReportDetail и сохраните отчет.

Я получил это для работы, наложив псевдоним сеттера на report_detail=, используя ReportDetail.find_or_create_by...но это некрасиво (а также создает ненужные записи ReportDetail просто путем создания новых отчетов с атрибутами детализации, и по какой-то причине я не смог заставить сохранение работать должным образом, используя .find_or_initialize_by...).Я также попробовал before_save на ReportDetail, чтобы сказать, если я сопоставляю что-то еще, установите self на это что-то еще.По-видимому, вы не можете настроить себя таким образом.

Любая мысль о лучшем способе сделать это?

см. эту суть для моей текущей перезаписи сеттерас псевдонимом

1 Ответ

1 голос
/ 20 сентября 2010

Только что столкнулся с этой проблемой сегодня, мое решение было основано на методе object_attributes=, который предоставляется accepts_nested_attributes_for, я думаю, что это создает меньше беспорядка вместо переопределения стандартного метода установки ассоциации. Немного погрузившись в источник рельсов, чтобы найти это решение, вот ссылка github . Код:

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

Некоторое объяснение, если вы предоставите идентификатор, будет считаться обновлением, поэтому больше не будет создавать новые объекты. Также я знаю, что для этого подхода нужен запрос в плюсе, но пока не могу найти лучшего решения.

Кроме того, похоже, что у вас есть связь 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

Согласно документации это должно работать для вас. Из рельсов3 документация:

: update_only

Позволяет указать, что существующая запись может обновляться только. Новая запись может быть создана только тогда, когда нет существующей записи это опция работает только для одного к одному ассоциации и игнорируется для Коллекционные ассоциации. Этот вариант по умолчанию отключено.

Надеюсь, это поможет, в любом случае, если вы найдете лучшее решение, пожалуйста, дайте мне знать.

...