2009-07-30 21 views
22

Me he actualizado a Rails 2.3.3 (desde 2.1.x) y estoy tratando de averiguar el método accepts_nested_attributes_for. Puedo usar el método para actualizar objetos anidados existentes, pero no puedo usarlo para crear nuevos objetos anidados. Dado el ejemplo artificioso:Cómo crear objetos anidados utilizando accept_nested_attributes_for

class Product < ActiveRecord::Base 
    has_many :notes 
    accepts_nested_attributes_for :notes 
end 

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id, :body 
end 

Si trato de crear un nuevo Product, con un anidado Note, de la siguiente manera:

params = {:name => 'Test', :notes_attributes => {'0' => {'body' => 'Body'}}} 
p = Product.new(params) 
p.save! 

Fracasa validaciones con el mensaje:

ActiveRecord::RecordInvalid: Validation failed: Notes product can't be blank 

Entiendo por qué sucede esto, es por el validates_presence_of :product_id en la clase Note, y porque en el momento de guardar el nuevo registro, el Product objeto no tiene un id. Sin embargo, no quiero eliminar esta validación; Creo que sería incorrecto eliminarlo.

También pude resolver el problema creando manualmente el Product primero, y luego agregando el Note, pero eso vence la simplicidad de accepts_nested_attributes_for.

¿Existe una forma estándar de Rails de crear objetos anidados en nuevos registros?

Respuesta

16

Este es un problema de dependencia común y circular. Hay un existing LightHouse ticket que vale la pena echarle un vistazo.

Espero que esto mejore mucho en Rails 3, pero mientras tanto, tendrá que hacer una solución. Una solución es configurar un atributo virtual que establezca al anidar para hacer que la validación sea condicional.

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id, :unless => :nested 
    attr_accessor :nested 
end 

Y luego debe establecer este atributo como un campo oculto en su formulario.

<%= note_form.hidden_field :nested %> 

Eso debería ser suficiente para tener el atributo nested establece al crear una nota a través de la forma anidada. No probado.

+11

En Rails 3 Este problema se resuelve por el añadido: inverse_of opción de has_many, has_one y belongs_to. Ver p. http://www.daokaous.com/rails3.0.0_doc/classes/ActiveRecord/Associations/ClassMethods.html#M001988 en la sección "Asociaciones bidireccionales" –

+2

He optado por deshabilitar la validación cuando: id == nil . Como eso solo debería ocurrir al escribir un nuevo registro anidado, espero que sea seguro. Es extraño que este problema haya llegado hasta 2.3.8. – aceofspades

+3

inverse_of no parece funcionar cuando ambos padres del registro aún no existen, y por lo tanto no se puede probar ninguna identificación. Parece que este truco anidado es el único camino a seguir. :( – DGM

3

solución de Ryan es realmente muy bueno. Fui y engordé mi controlador para que este anidamiento no tuviera que aparecer en la vista. Principalmente porque mi vista es a veces json, así que quiero ser capaz de salir lo menos posible allí.

class Product < ActiveRecord::Base 
    has_many :notes 
    accepts_nested_attributes_for :note 
end 

class Note < ActiveRecord::Base 
    belongs_to :product 
    validates_presence_of :product_id unless :nested 
    attr_accessor :nested 
end 

class ProductController < ApplicationController 

def create 
    if params[:product][:note_attributes] 
     params[:product][:note_attributes].each { |attribute| 
      attribute.merge!({:nested => true}) 
    } 
    end 
    # all the regular create stuff here 
end 
end 
7
+2

punto muy importante es que debe validar la presencia de "producto" no "product_id" – kuboon

+0

Creo que kuboon tiene la respuesta correcta. Valide la presencia de "producto", no "producto_id" junto con cambiar la asociación en el Producto a "has_many: notes, inverse_of:: product" debería hacerlo. – quainjn

Cuestiones relacionadas