update rails many how has_many has form for fields_for example belongs and accepts_nested_attributes_for accepts accept ruby-on-rails model nested-attributes

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.