Подтвердить что ты не робот

Rails: создать ассоциацию, если ни один не найден, чтобы избежать ошибок nil

У меня есть приложение, в котором мои пользователи могут иметь набор настроек. Оба сохраняются в виде моделей ActiveRecord следующим образом:

class User < AR::Base
   has_one :preference_set
end

class PreferenceSet < AR::Base
   belongs_to :user
end

Теперь я могу получить доступ к настройкам пользователя:

@u = User.first
@u.preference_set => #<PreferenceSet...>
@u.preference_set.play_sounds => true

Но это не удается, если набор предпочтений еще не создан, так как @u.preference_set будет возвращать нуль, и я буду называть play_sounds на nil.

Что я хочу архивировать, так это то, что User.preference_set всегда возвращает экземпляр PreferenceSet. Я попытался определить его следующим образом:

class User < ..
   has_one :preference_set

   def preference_set
     preference_set || build_preference_set
   end
end

Это вызывает 'Stack level too deep', поскольку он вызывает себя рекурсивно.

Мой вопрос:

Как я могу гарантировать, что @user.preference_set возвращает либо соответствующую запись preference_set-record, либо, если она не существует, создает новую?

Я знаю, что могу просто переименовать свою ассоциацию (например. preference_set_real) и избежать рекурсивных вызовов таким образом, но для простоты в моем приложении я хотел бы сохранить именование.

Спасибо!

4b9b3361

Ответ 1

Ну, лучший способ сделать это - создать связанную запись при создании первичного:

class User < ActiveRecord::Base
   has_one       :preference_set, :autosave => true
   before_create :build_preference_set
end

Это установит его так, когда будет создан User, а также PreferenceSet. Если вам нужно инициализировать связанную запись с помощью аргументов, тогда вызовите другой метод в before_create, который вызывает build_preference_set(:my_options => "here"), а затем возвращает true.

Затем вы можете просто нормализовать все существующие записи, итерации по всем, у которых нет PreferenceSet, и построить его, вызвав #create_preference_set.

Если вы хотите создать только PreferenceSet, когда это абсолютно необходимо, вы можете сделать что-то вроде:

class User < ActiveRecord::Base
   has_one :preference_set

   def preference_set_with_initialize
     preference_set_without_initialize || build_preference_set
   end

   alias_method_chain :preference_set, :initialize
end

Ответ 2

или просто

class User < ApplicationRecord
   has_one :preference_set

   def preference_set
     super || build_preference_set
   end
end