Rails: создайте ассоциацию, если ничего не найдено, чтобы избежать ошибок nil - PullRequest
13 голосов
/ 28 сентября 2010

У меня есть приложение, в котором мои пользователи могут иметь набор предпочтений.Оба хранятся в виде 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 будет возвращать nil, и я буду звонить 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-запись, либо, если ее нет, создает новую?

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

Спасибо!

Ответы [ 2 ]

43 голосов
/ 24 июля 2013

или просто

class User < ApplicationRecord
   has_one :preference_set

   def preference_set
     super || build_preference_set
   end
end

Это работает, потому что ActiveRecord определяет метод ассоциации в mixin.

28 голосов
/ 28 сентября 2010

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

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
...