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
) и избежать рекурсивных вызовов таким образом, но для простоты в моем приложении я хотел бы сохранить именование.
Спасибо!
Ответы
Ответ 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