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

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?

Спасибо

4b9b3361

Ответ 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

Метод инициализации никогда не вызывается, когда запись загружается из базы данных. Обратите внимание, что в коде выше атрибуты назначаются после сборки продукта. В таком назначении атрибута атрибута может влиять на созданный экземпляр продукта.