ruby on rails - many - Rails after_initialize solo en "nuevo"
rails nested attributes has_many (6)
Tengo los siguientes 2 modelos
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
Un deporte no puede existir sin el producto, entonces en mi sports_controller.rb
tuve:
def new
@sport = Sport.new
@sport.product = Product.new
...
end
Traté de mover la creación del producto al modelo deportivo, usando after_initialize
:
after_initialize :create_product
def create_product
self.product = Product.new
end
Aprendí rápidamente que se llama a after_initialize
cada vez que se after_initialize
una instancia de un modelo (es decir, de una llamada de find
). Entonces ese no era el comportamiento que estaba buscando.
¿De qué manera debo modelar el requisito de que todos los sport
tengan un product
?
Gracias
Debería anular el método de inicialización como
class Sport < ActiveRecord::Base
# ...
def initialize(attributes = {})
super
self.build_product
self.attributes = attributes
end
# ...
end
El método de inicialización nunca se invoca cuando el registro se carga desde la base de datos. Tenga en cuenta que en el código anterior los atributos se asignan después de que el producto es compilado. En dicha configuración, la asignación de atributos puede afectar la instancia del producto creado.
En lugar de utilizar after_initialize
, ¿qué hay de after_create
?
after_create :create_product
def create_product
self.product = Product.new
save
end
¿Parece que eso solucionaría tu problema?
Parece que estás muy cerca. Debería poder deshacerse por completo de la llamada after_initialize, pero primero creo que si su modelo Sport tiene una relación "has_one" con: product como usted indicó, entonces su modelo Product también debería ser "belong_to" sport. Agregue esto a su modelo de Producto
belongs_to: :sport
El siguiente paso, ahora debería ser capaz de crear una instancia de un modelo Sport como tal
@sport = @product.sport.create( ... )
Esto se basa en la información de Conceptos básicos de la asociación de Ruby on Rails Guides, que podría leer si no soy exactamente correcto
Poner la lógica en el controlador podría ser la mejor respuesta como dijiste, pero podrías hacer que after_initialize
funcione haciendo lo siguiente:
after_initialize :add_product
def add_product
self.product ||= Product.new
end
De esta forma, solo establece el producto si no existe ningún producto. Puede no valer la pena la sobrecarga y / o ser menos claro que tener la lógica en el controlador.
Editar: Según la respuesta de Ryan, en cuanto a rendimiento, lo siguiente probablemente sea mejor:
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
Seguramente after_initialize :add_product, if: :new_record?
es la manera más limpia aquí.
Mantenga el condicional fuera de la función add_product
Si realiza self.product ||= Product.new
, seguirá buscando un producto cada vez que realice un find
ya que debe verificar si es nulo o no. Como resultado, no hará ninguna carga ansiosa. Para hacer esto solo cuando se crea un nuevo registro, simplemente puede verificar si se trata de un nuevo registro antes de configurar el producto.
after_initialize :add_product
def add_product
self.product ||= Product.new if self.new_record?
end
Hice algunos benchmarking básicos y comprobando if self.new_record?
no parece afectar el rendimiento de ninguna manera notable.