2009-02-17 6 views
5

Mi objetivo es crear recursos anidados a través de una solicitud REST. Las solicitudes REST se representan a través de un documento XML. Eso funciona bien para los recursos individuales, pero no pude gestionarlo para los anidados. OK, les daré un pequeño ejemplo a continuación.¿Cómo usar REST con recursos anidados que están representados en XML?

En primer lugar crear un nuevo rieles proyectan

rails forrest 

A continuación se generan los andamios de dos recursos, los árboles y los nidos de las aves.

./script/generate scaffold tree name:string 
./script/generate scaffold bird_nest tree_id:integer bird_type:string eggs_count:integer 

en el archivo ./forrest/app/models/tree.rb insertamos la línea "has_many" a continuación debido a que un árbol puede tener muchos nidos de aves :-)

class Tree < ActiveRecord::Base 
    has_many :bird_nests 
end 

en el archivo ./forrest/app/models/bird_nest.rb insertamos la línea "belongs_to" a continuación porque cada nido de pájaro debe pertenecer a un árbol.

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

Posteriormente hemos creado la base de datos e iniciar el servidor:

rake db:create 
rake db:migrate 
./script/server 

Simplemente copia y pega este sniplet XML en un archivo denominado "tree.xml" ...

<tree> 
    <name>Apple</name> 
</tree> 

... y publicarlo en el servicio por cURL para crear un nuevo árbol:

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @tree.xml http://localhost:3000/trees/ -X POST 

Esto funciona bien. También para el nido de pájaro XML (nombre de archivo "bird-nest.xml") por separado. Si enviamos esto ...

<bird-nest> 
    <tree-id>1</tree-id> 
    <bird-type>Sparrow</bird-type> 
    <eggs-count>2</eggs-count> 
</bird-nest> 

... también a través de la siguiente instrucción cURL. ¡Ese recurso se crea correctamente!

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @bird-nest.xml http://localhost:3000/bird_nests/ -X POST 

OK everything está bien hasta el momento. Ahora viene el punto donde la goma se encuentra con la carretera. Creamos ambos recursos en una sola solicitud. Así que aquí es el XML para nuestro árbol que contiene el nido de un ave:

<tree> 
    <name>Cherry</name> 
    <bird-nests> 
    <bird-nest> 
     <bird-type>Blackbird</bird-type> 
     <eggs-count>2</eggs-count> 
    </bird-nest> 
    </bird-nests> 
</tree> 

Nos activan la solicitud correspondiente mediante el uso de rizo de nuevo ...

curl -H 'Content-type: application/xml' -H 'Accept: application/xml' -d @tree-and-bird_nest.xml http://localhost:3000/trees/ -X POST 

... y ahora vamos a tener una error de servidor en la (generado) "crear" método del controlador del árbol: AssociationTypeMismatch (birdnest espera, array GOT)

en mi punto de vista esta es la parte importante de registro del servidor con respecto a los atributos recibidos y messa de error ge:

Processing TreesController#create (for 127.0.0.1 at 2009-02-17 11:29:20) [POST] 
    Session ID: 8373b8df7629332d4e251a18e844c7f9 
    Parameters: {"action"=>"create", "controller"=>"trees", "tree"=>{"name"=>"Cherry", "bird_nests"=>{"bird_nest"=>{"bird_type"=>"Blackbird", "eggs_count"=>"2"}}}} 
    SQL (0.000082) SET NAMES 'utf8' 
    SQL (0.000051) SET SQL_AUTO_IS_NULL=0 
    Tree Columns (0.000544) SHOW FIELDS FROM `trees` 


    ActiveRecord::AssociationTypeMismatch (BirdNest expected, got Array): 
     /vendor/rails/activerecord/lib/active_record/associations/association_proxy.rb:150:in `raise_on_type_mismatch' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `replace' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `each' 
     /vendor/rails/activerecord/lib/active_record/associations/association_collection.rb:146:in `replace' 
     /vendor/rails/activerecord/lib/active_record/associations.rb:1048:in `bird_nests=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2117:in `send' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2117:in `attributes=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2116:in `each' 
     /vendor/rails/activerecord/lib/active_record/base.rb:2116:in `attributes=' 
     /vendor/rails/activerecord/lib/active_record/base.rb:1926:in `initialize' 
     /app/controllers/trees_controller.rb:43:in `new' 
     /app/controllers/trees_controller.rb:43:in `create' 

Así que mi pregunta es qué estoy haciendo mal con respecto a la anidación de los recursos XML. ¿Cuál sería la sintaxis correcta de XML? ¿O tengo que modificar el controlador del árbol manualmente ya que este caso no está cubierto por el generado?

+0

Parece que es posible que necesite editar la acción Crear de TreesController para gestionar la creación de objetos BirdsNest. ¿Puedes publicar una copia de TreesController create action? – hernan43

+0

La acción de creación es la acción predeterminada que se generó en los pasos anteriores (consulte "generar andamio"). –

Respuesta

3

Una forma en que puede lograr esto es anulando el método bird_nests = en su modelo de árbol.

def bird_nests=(attrs_array) 
    attrs_array.each do |attrs| 
    bird_nests.build(attrs) 
    end 
end 

El único problema aquí es que se pierde el comportamiento predeterminado del organismo, que puede o no puede ser un problema en su aplicación.

Si está ejecutando una versión más reciente de los carriles que sólo puede convertir en la asignación de masas como se describe aquí:

http://github.com/rails/rails/commit/e0750d6a5c7f621e4ca12205137c0b135cab444a

Y aquí:

http://ryandaigle.com/articles/2008/7/19/what-s-new-in-edge-rails-nested-models

class Tree < ActiveRecord::Base 
    has_many :bird_nests, :accessible => true 
end 

Esta es la opcion preferida.

+0

La primera solución funciona bien con pequeñas modificaciones con respecto a los nidos que no son de matriz, gracias. El segundo solo plantea: "ArgumentError (clave desconocida (s): accesible)", así que supongo que mis rieles versión 2.2.2 aún no admiten la función de "asignación masiva". –

1

El controlador por defecto tendrá una línea como

@tree = Tree.new(params[:tree]) 

cuales apprently no automágicamente analizar los parámetros que has enviado. Deseará cambiar su controlador para separar el hashf de params, crear y guardar el árbol, luego crear el nido, utilizando la identificación del árbol (que no se creará hasta después de guardarlo) y guardar el árbol.

¿Borrar como barro?

2

La anulación del método bird_nests = del modelo de árbol es una solución válida, en referencia a la publicación anterior de Patrick Richie (gracias). Entonces, no son necesarios cambios en el controlador. Aquí está el código en detalle que se encargará de las solicitudes XML dados mencionados en el ejemplo anterior (también el manejo de nidos no array):

def bird_nests=(params) 
    bird_nest=params[:bird_nest] 
    if !bird_nest.nil? 
     if bird_nest.class==Array 
     bird_nest.each do |attrs| 
      bird_nests.build(attrs) 
     end 
     else 
     bird_nests.build(bird_nest) 
     end 
    end 
    end 
2

Aunque esta pregunta se hizo hace dos años y medio, mucho ha cambiado ahora: por primera vez en Rails 2.3 con has_many :bird_nests, :accessible => true y ahora en Rails 3 con accepts_nested_attributes_for método ... así que en estos días en los carriles 3 que permitan lograr el objetivo anterior con el siguiente código:

class Tree < ActiveRecord::Base 
    has_many :bird_nests 
    accepts_nested_attributes_for :bird_nests 
end 

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

Esto genera bird_nests_attributes descriptores de acceso (captador/setter) para el objeto Tree. Por lo que el XML se vería siguiente:

<tree> 
    <name>Cherry</name> 
    <bird_nests_attributes type='array'> 
     <bird_nest> 
      <bird-type>Blackbird</bird-type> 
      <eggs-count>2</eggs-count> 
     </bird_nest> 
     <bird_nest> 
      <bird-type>Bluebird</bird-type> 
      <eggs-count>3</eggs-count> 
     </bird_nest> 
    </bird_nests_attributes> 
</tree> 

Rails convertir el XML anterior a la de hash params apropiada ... y el objeto de árbol con bird_nests asociados objetos se crean con las siguientes afirmaciones

@tree = Tree.new(params[:tree]) 
@tree.save 

Este es el código mínimo para que funcione. Si está utilizando attr_accessible en sus modelos, que debe siempre, entonces no olvide añadir :bird_nests_attributes a attr_accessible lista como siguiente:

attr_accessible :a_tree_attribute, :another_tree_attr, :bird_nests_attributes 

Del mismo modo se puede agregar validaciones de los atributos a sus respectivos modelos. Y si una validación falla los errores de atributos anidados también estarían disponibles en la lista @tree.errors. Espero que esto ayude a otros que buscaron la misma pregunta en Google y esta publicación obsoleta fue el resultado principal.

Cuestiones relacionadas