2009-02-17 3 views
5

Il mio obiettivo è creare risorse nidificate tramite una richiesta REST. Le richieste REST sono rappresentate tramite un documento XML. Funziona bene per le singole risorse, ma non sono riuscito a gestirle per quelle nidificate. OK ti darò un piccolo esempio dopo.Come utilizzare REST con risorse nidificate rappresentate in XML?

anzitutto creare un nuovo rotaie proiettano

rails forrest 

Poi si generano i ponteggi di due risorse, gli alberi e nidi degli uccelli.

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

Nel File ./forrest/app/models/tree.rb inseriamo la linea "has_many" di seguito a causa di un albero può avere nidi di molti uccelli :-)

class Tree < ActiveRecord::Base 
    has_many :bird_nests 
end 

Nel File ./forrest/app/models/bird_nest.rb inseriamo la riga "belongs_to" qui sotto perché ogni nido d'uccello dovrebbe appartenere ad un albero.

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

Successivamente abbiamo impostare il database e avviare il server:

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

Basta copiare e incollare questo sniplet XML in un file denominato "tree.xml" ...

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

... e postalo al servizio da cURL per creare un nuovo albero:

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

Funziona bene. Anche per il nido d'uccello XML (nome del file "bird-nest.xml") separatamente. Se inviamo questo ...

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

... anche tramite la seguente istruzione cURL. Quella risorsa è stata creata correttamente!

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

OK tutto va bene finora. Ora arriva il punto in cui la gomma incontra la strada. Creiamo entrambe le risorse in un'unica richiesta. Così qui è l'XML per il nostro albero che contiene il nido d'uccello uno:

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

Abbiamo innescare la richiesta appropriato utilizzando di nuovo curl ...

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

... e adesso ci prendiamo un errore del server nel (generato) "creare" il metodo del controllore della albero: AssociationTypeMismatch (birdnest previsto, Array ottenuto)

nel mio punto di vista questa è la parte importante del log del server per quanto riguarda gli attributi ricevuti e messa errore 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' 

Quindi la mia domanda è cosa sto facendo male per quanto riguarda l'annidamento delle risorse XML. Quale sarebbe la sintassi XML corretta? O devo modificare manualmente il controller dell'albero in quanto questo non è coperto da quello generato?

+0

Sembra che potrebbe essere necessario modificare TreesController per creare un'azione per gestire la creazione di oggetti BirdsNest. Puoi pubblicare una copia di TreesController per creare un'azione? – hernan43

+0

L'azione di creazione è l'azione predefinita che è stata generata nei passaggi precedenti (vedere "generare scaffold"). –

risposta

3

Un modo per eseguire questa operazione è sovrascrivere il metodo bird_nests = sul modello di albero.

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

L'unico problema qui è che si perde il comportamento predefinito del palleggiatore, che può o non può essere un problema nella vostra app.

Se si sta eseguendo una versione più recente di Rails si può solo accendere l'assegnazione di massa come descritto qui:

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

E qui:

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 

Questa è l'opzione preferita.

+0

La prima soluzione funziona bene con piccole modifiche relative ai nidi non array, grazie. La seconda solleva: "ArgumentError (Chiave sconosciuta: accessibile)" quindi suppongo che la mia versione di rails 2.2.2 non supporti ancora la funzionalità di "assegnazione di massa". –

1

Il controller di default avrà una linea come

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

che apprently non automagicamente analizzare i parametri che avete inviato. Dovrai cambiare il controller per separare i parametri hashf, creare e salvare l'albero, quindi creare il nido, usando l'ID dell'albero (che non verrà creato fino a dopo averlo salvato) e salvare l'albero.

Chiaro come fango?

2

L'override del metodo bird_nests = del modello ad albero è una soluzione valida, che fa riferimento al precedente post di Patrick Richie (grazie). Quindi non sono necessarie modifiche sul controller. Ecco il codice in dettaglio che gestirà le date richieste XML citati nell'esempio di cui sopra (la manipolazione anche nidi non di 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

Anche se questa domanda è stato chiesto due anni e mezzo fa, molto è cambiato ora: prima in Rails 2.3 con has_many :bird_nests, :accessible => true ed ora in Rails 3 con accepts_nested_attributes_for metodo ... così in questi giorni in Rails 3 si permettano di conseguire gli scopi di cui sopra con il seguente codice:

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

class BirdNest < ActiveRecord::Base 
    belongs_to :tree 
end 

Questo genera bird_nests_attributes di accesso (getter/setter) per oggetto Tree. Così il xml sarebbe simile seguente:

<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à il file XML di cui sopra per l'appropriato params hash ... ed oggetto Albero con bird_nests associati oggetti sarebbero stati creati con le seguenti affermazioni

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

Questo è il codice minimo per farlo funzionare. Se si utilizza attr_accessible sui modelli, che si deve sempre, quindi non dimenticate di aggiungere :bird_nests_attributes-attr_accessible lista come segue:

attr_accessible :a_tree_attribute, :another_tree_attr, :bird_nests_attributes 

Allo stesso modo è possibile aggiungere validazioni per gli attributi ai loro rispettivi modelli. E se una convalida fallisce, anche gli attributi degli attributi annidati saranno disponibili nell'elenco @tree.errors. Spero che questo aiuti gli altri che hanno cercato la stessa domanda in google e questo post obsoleto è arrivato come risultato principale.