2012-08-03 12 views
8

ActiveRecord no parece entender que, dado un conjunto de parámetros para un registro existente con atributos anidados, puede crear un nuevo registro anidado que tiene un anidado registro existente. (Árbol de relaciones: Existing -> New -> Existing)AcitveRecord no acepta attributes_nested_attributes_for nuevos registros asociados con registros existentes anidados

¿Esto es un error o me falta algo?

Déjame mostrarte un ejemplo simple.

Éstos son mis modelos:

class User < ActiveRecord::Base 
    has_many :posts 
    attr_accessible :name, :posts_attributes 
    accepts_nested_attributes_for :posts 
end 

class Post < ActiveRecord::Base 
    belongs_to :group 
    belongs_to :user 
    attr_accessible :content, :title, :group_attributes 
    accepts_nested_attributes_for :group 
end 

class Group < ActiveRecord::Base 
    has_many :posts 
    attr_accessible :name 
end 

que he realizado un registro en cada mesa, y los relacionados con la consecuencia, por lo que cada tabla tiene un registro en ella con un id=1 --Este es conocido. Ahora, si tengo un usuario existente, un nuevo mensaje, y un grupo existente, y trato de actualizar ese registro utilizando accepts_nested_attributes_for, que no le gusta:

1.9.3-p125 :044 > params 
{ 
        :id => 1, 
       :name => "Billy", 
    :posts_attributes => [ 
     [0] { 
          :title => "Title", 
         :content => "Some magnificent content for you!", 
      :group_attributes => { 
        :id => 1, 
       :name => "Group 1" 
      } 
     } 
    ] 
} 
1.9.3-p125 :045 > u 
#<User:0x00000002f7f380> { 
      :id => 1, 
      :name => "Billy", 
    :created_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00, 
    :updated_at => Fri, 03 Aug 2012 20:21:37 UTC +00:00 
} 
1.9.3-p125 :046 > u.update_attributes params 
    (0.1ms) begin transaction 
    (0.1ms) rollback transaction 
ActiveRecord::RecordNotFound: Couldn't find Group with ID=1 for Post with ID= 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:462:in `raise_nested_attributes_record_not_found' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:332:in `assign_nested_attributes_for_one_to_one_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `group_attributes=' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:94:in `block in assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:93:in `assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/base.rb:498:in `initialize' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `new' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/reflection.rb:183:in `build_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/association.rb:233:in `build_record' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/associations/collection_association.rb:112:in `build' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:405:in `block in assign_nested_attributes_for_collection_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:400:in `assign_nested_attributes_for_collection_association' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/nested_attributes.rb:288:in `posts_attributes=' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:85:in `block in assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `each' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/attribute_assignment.rb:78:in `assign_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:216:in `block in update_attributes' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:208:in `transaction' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/transactions.rb:293:in `with_transaction_returning_status' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/activerecord-3.2.7/lib/active_record/persistence.rb:215:in `update_attributes' 
    from (irb):15 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:47:in `start' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands/console.rb:8:in `start' 
    from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.7/lib/rails/commands.rb:41:in `<top (required)>' 
    from script/rails:6:in `require' 
    from script/rails:6:in `<main>'1.9.3-p125 :047 > 

Se piensa que no puede encontrar un grupo (con un ID conocido), relacionado con un nuevo mensaje. Funciona cuando elimino el ID del group_attributes (pero crea un nuevo registro de grupo). Funciona cuando doy una ID al posts_attributes y elimino la identificación del group_attributes (y de nuevo crea un nuevo grupo). También funciona cuando todos tienen ID.

La relación está funcionando:

1.9.3-p125 :059 > p = Post.new({ group_attributes: { name: 'Testing' } }) 
#<Post:0x00000004212380> { 
      :id => nil, 
     :title => nil, 
     :content => nil, 
     :group_id => nil, 
     :user_id => nil, 
    :created_at => nil, 
    :updated_at => nil 
} 
1.9.3-p125 :060 > p.group 
[ 
    [0] #<Group:0x00000004211868> { 
       :id => nil, 
       :name => "Testing", 
     :created_at => nil, 
     :updated_at => nil 
    } 
] 

También funciona por completo cuando se utiliza posts_attributes y group_attributes durante User creación, si todos los registros son nuevos.

¿No debería funcionar todavía en el primer ejemplo? ActiveRecord debe ser lo suficientemente inteligente como para resolver esto ...!

+0

Falta un 'has_many: comments' en el modelo de Usuario. – jordanpg

+0

oops, bueno, el problema sigue en pie. :) Simplificaré el ejemplo. – wulftone

+0

pregunta larga, pero ¿cómo un nuevo registro tiene una asociación existente? – pjammer

Respuesta

4

Esto es lo que creo que está sucediendo: está pasando una identificación para un grupo, indicando a ActiveRecord que el grupo existe. ActiveRecord está intentando encontrar ese grupo para actualizarlo con los otros datos que tiene en group_attributes. Puesto que usted está haciendo esto dentro los post_attributes, ActiveRecord está tratando de encontrar ese grupo a través de la asociación entre elposterior y el grupo. Es decir, ActiveRecord busca primero el grupo asociado, donde id = post.group_id, luego de ese resultado busca el que tiene ID = 1. Esto puede parecer un poco extraño y torpe para una relación parental, como en su caso, pero Estoy seguro de que puede ver que este es un comportamiento útil cuando va en la otra dirección, donde los atributos anidados representan uno o más de los potencialmente muchos niños.

Sin embargo, su objeto de publicación, creado a partir de los datos en post_attributes, aún no está asociado a un grupo - post.group_id es nil. Entonces, cuando ActiveRecord hace esa primera búsqueda para obtener el grupo asociado, aparece vacío. En consecuencia, no encuentra un grupo con ID = 1 en los resultados (vacíos). Técnicamente, el registro está allí, pero no está allí en términos de asociación con la publicación.

Puede probar esto incluyendo group_id => 1 en post_attributes.Creo que si lo haces, ActiveRecord buscará el grupo por asociación, luego seleccionará el grupo con ID = 1 de los resultados, con éxito, y luego actualizará ese grupo con los datos adicionales en group_attributes.

Tenga en cuenta que el único motivo para incluir los atributos anidados para el grupo dentro de la publicación como lo está haciendo es si permite que el usuario actualice el nombre del grupo al mismo tiempo que están creando una nueva publicación. Si todo lo que desea hacer es vincular la nueva publicación al grupo existente, entonces solo necesita incluir group_id en post_attributes y puede deshacerse de group_attributes.

+0

Esto me parece bastante lógico, pero este escenario ya pasó. :) Recuerdo que _idid_ quería actualizar atributos en la asociación y el registro raíz simultáneamente (no estaba usando estas tablas exactas de Usuario, Publicación y Grupo, pero era una estructura similar). Creo que la necesidad de hacer tal acción fue puesta en un segundo plano para su futura implementación, luego olvidada. ¡Puede volver a perseguirnos! Gracias por tu respuesta. Me aseguraré de referirme a esto cuando me encuentre con esto de nuevo. – wulftone

+0

Probándolo nuevamente, obtengo nuevos 'Grupos' creados incluso si tengo un' group_id' en el 'Post', aunque no arroja un error. Simplemente ignora el 'group_id' y crea uno de todos modos. Todavía no parece posible hacer. – wulftone

+0

Dammit. Realmente pensé que tenía ese clavado. Voy a tomarme un tiempo para jugar con el escenario este fin de semana y ver qué puedo inventar, ahora está pegado a mí. ;) – Yardboy

Cuestiones relacionadas