Rails after_initialize только на "новом"
У меня есть следующие 2 модели
class Sport < ActiveRecord::Base
has_many :charts, order: "sortWeight ASC"
has_one :product, :as => :productable
accepts_nested_attributes_for :product, :allow_destroy => true
end
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :productable, :polymorphic => true
end
Спорт не может существовать без продукта, поэтому в моем sports_controller.rb
у меня было:
def new
@sport = Sport.new
@sport.product = Product.new
...
end
Я попытался переместить создание продукта в спортивную модель, используя after_initialize
:
after_initialize :create_product
def create_product
self.product = Product.new
end
Я быстро понял, что after_initialize
вызывается всякий раз, когда создается экземпляр модели (т.е. от вызова find
). Так что это было не то поведение, которое я искал.
Каким образом я должен моделировать требование, чтобы все sport
имели product
?
Спасибо
Ответы
Ответ 1
Включение логики в контроллер может быть лучшим ответом, как вы заявили, но вы можете заставить after_initialize
работать, выполняя следующие действия:
after_initialize :add_product
def add_product
self.product ||= Product.new
end
Таким образом, он только устанавливает продукт, если продукт не существует. Это может не стоить накладных расходов и/или быть менее ясным, чем наличие логики в контроллере.
Изменить: Ответ на Ryan, по результатам работы, вероятно, будет лучше:
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
Ответ 2
Если вы выполняете self.product ||= Product.new
, он будет продолжать поиск продукта каждый раз, когда вы делаете find
, потому что ему нужно проверить, нет ли он или нет. В результате он не будет загружать. Чтобы сделать это только при создании новой записи, вы можете просто проверить, не является ли она новой записью перед настройкой продукта.
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
Я сделал базовый бенчмаркинг, и проверка if self.new_record?
не влияет на производительность каким-либо заметным образом.
Ответ 3
Конечно, after_initialize :add_product, if: :new_record?
- самый чистый путь здесь.
Сохранить условие из функции add_product
Ответ 4
Вместо использования after_initialize
, как насчет after_create
?
after_create :create_product
def create_product
self.product = Product.new
save
end
Похоже ли, что это решит вашу проблему?
Ответ 5
Похоже, вы очень близки. Вы должны быть в состоянии покончить с вызовом after_initialize вообще, но сначала я считаю, что если ваша модель Sport имеет отношения has_one с: продуктом, как вы указали, тогда ваша модель продукта также должна быть "принадлежать" к спорту. Добавьте это в свою модель продукта
belongs_to: :sport
Следующий шаг, теперь вы должны создать экземпляр модели Sport так:
@sport = @product.sport.create( ... )
Это основано на информации из Основы ассоциации от Ruby on Rails Guides, которую вы могли бы прочитать, если я не совсем прав
Ответ 6
Вы должны просто переопределить метод инициализации, например
class Sport < ActiveRecord::Base
# ...
def initialize(attributes = {})
super
self.build_product
self.attributes = attributes
end
# ...
end
Метод инициализации никогда не вызывается, когда запись загружается из базы данных.
Обратите внимание, что в коде выше атрибуты назначаются после сборки продукта.
В таком назначении атрибута атрибута может влиять на созданный экземпляр продукта.