Многозначная ассоциация, где идентификатор ассоциации (а не значения) изменяется - PullRequest
1 голос
/ 29 ноября 2011

У меня есть модель User, которая belongs_to :location (то есть каждый пользователь может иметь ровно одно местоположение, но местоположение может совместно использоваться несколькими пользователями).Таблица users имеет столбец location_id для отслеживания этого.

Если пользователь хочет изменить свое местоположение, я считаю, что поведение по умолчанию с ассоциацией Rails заключается в изменении фактической ассоциации;в этом случае, однако, я хочу, чтобы существующая ассоциация (т.е. существующая запись Location) осталась прежней, а новая была создана.

Например, скажем, у нас есть пользователи Алиса,Боб, Чарли и Дейв, у всех из которых есть location_id == 1, что соответствует местоположению Улица Сезам 123. Затем, когда Дейв меняет свое местоположение на Улицу Сезам 124, Location 1 не должен быть затронут или изменен каким-либо образом, и Location 2 должен быть создан с новой информацией, а запись пользователя Дейва обновлена, чтобы отразить, что location_id теперь равно 2 вместо 1.

Я хочу внести изменения на уровне модели (предположительно, User модель), чтобы при изменении какой-либо информации о местоположении (User также accepts_nested_attributes_for :location) связь Location корректно обновлялась без перезаписи существующей информации.

Есть ли способ сделать это,встроен в Rails или в плагин?

1 Ответ

1 голос
/ 29 ноября 2011

accepts_nested_attributes_for создает несколько вспомогательных методов для обработки вложенных форм. В вашем случае вы захотите переопределить сеттер. Это должно начать вас.

class User

  accepts_nested_attributes_for :location

  def location_attributes= attrs
    new_location   = Location.where(attrs).first
    new_location ||= Location.create attrs

    self.location = new_location
  end

end

И как-то так в вашей форме

= f.fields_for :location do |lf|
  .field
    = lf.label      :address
    = lf.text_field :address
  .field
    = lf.label      :city
    = lf.text_field :city

Некоторые заметки:

  • Если у вашего пользователя вообще нет местоположения, поля для местоположения не будут отображаться. Добавьте вызов к @user.build_location в форме или в контроллере.
  • Возможно, вы захотите улучшить логику поиска / создания, это довольно надуманный пример

Если ваша модель местоположения представляет собой одно поле, вы можете упростить все это с помощью виртуального атрибута

class User
  def location_address= address
    new_location = Location.find_or_create_by_address(address)
    self.location = new_location
  end

  def location_address
    location.address
  end
end

В этом случае вам нужно только текстовое поле в форме

= f.text_field :location_address
...