2009-05-10 10 views
30

Come passare JSON a un'applicazione RAILS in modo che crei oggetti figlio nidificati in una relazione has_many?Creazione di oggetti nidificati con JSON in Rails

Ecco quello che ho finora:

Due oggetti del modello.

class Commute < ActiveRecord::Base 
    has_many :locations 
    accepts_nested_attributes_for :locations, :allow_destroy => true 
end 

class Location < ActiveRecord::Base 
    belongs_to :commute 
end 

Con Commute, ho impostato un controller standard. Mi piacerebbe essere in grado di creare un oggetto Commute così come diversi oggetti Location child in una singola chiamata REST usando JSON. Ho provato cose come questa:

curl -H "Content-Type:application/json" -H "Accept:application/json" 
-d "{\"commute\":{\"minutes\":0, 
\"startTime\":\"Wed May 06 22:14:12 EDT 2009\", 
\"locations\":[{\"latitude\":\"40.4220061\", 
\"longitude\":\"40.4220061\"}]}}" http://localhost:3000/commutes 

O, più leggibile, il JSON è:

{ 
    "commute": { 
     "minutes": 0, 
     "startTime": "Wed May 06 22:14:12 EDT 2009", 
     "locations": [ 
      { 
       "latitude": "40.4220061", 
       "longitude": "40.4220061" 
      } 
     ] 
    } 
} 

Quando eseguo che, ottengo questo output:

Processing CommutesController#create (for 127.0.0.1 at 2009-05-10 09:48:04) [POST] 
    Parameters: {"commute"=>{"minutes"=>0, "locations"=>[{"latitude"=>"40.4220061", "longitude"=>"40.4220061"}], "startTime"=>"Wed May 06 22:14:12 EDT 2009"}} 

ActiveRecord::AssociationTypeMismatch (Location(#19300550) expected, got HashWithIndifferentAccess(#2654720)): 
    app/controllers/commutes_controller.rb:46:in `new' 
    app/controllers/commutes_controller.rb:46:in `create' 

Si guarda come le posizioni in cui viene letto l'array JSON, ma non interpretato come un oggetto Location.

Posso facilmente cambiare il client o il server, quindi la soluzione potrebbe provenire da entrambi i lati.

Quindi, RAILS rende questo facile per me da fare? O devo aggiungere qualche supporto per questo al mio oggetto Commute? Forse aggiungi un metodo from_json?

Grazie per qualsiasi aiuto.


Come ho lavorato su questo, una soluzione che funziona è modificare il mio controller. Ma questo non sembra il modo "rotaie" per farlo, quindi per favore fatemi sapere se c'è un modo migliore.

def create 
    locations = params[:commute].delete("locations"); 
    @commute = Commute.new(params[:commute]) 

    result = @commute.save 

    if locations 
     locations.each do |location| 
     @commute.locations.create(location) 
     end 
    end 


    respond_to do |format| 
     if result 
     flash[:notice] = 'Commute was successfully created.' 
     format.html { redirect_to(@commute) } 
     format.xml { render :xml => @commute, :status => :created, :location => @commute } 
     else 
     format.html { render :action => "new" } 
     format.xml { render :xml => @commute.errors, :status => :unprocessable_entity } 
     end 
    end 
    end 

risposta

35

capito, le posizioni oggetto avrebbe dovuto chiamarsi locations_attributes da combaciare con le rotaie nidificati creazione di oggetti schema di denominazione. Dopo averlo fatto, funziona perfettamente con un controller Rails predefinito.

{ 
    "commute": { 
     "minutes": 0, 
     "startTime": "Wed May 06 22:14:12 EDT 2009", 
     "locations_attributes": [ 
      { 
       "latitude": "40.4220061", 
       "longitude": "40.4220061" 
      } 
     ] 
    } 
} 
+1

Sembra che tu sia in grado di controllare il JSON di origine, ma cosa succede se non puoi, come gestiresti "posizioni"? – Jerome

+0

Dai un'occhiata, ho appena postato una modifica da una risposta che è stata cancellata. –

+0

Per chiunque abbia ancora problemi con questo, è necessario consentire manualmente gli attributi nel controller. Esempio: 'params.require (: commute) .permit (: minutes,: startTime, locations_attributes: [: latitude,: longitude])' – Kevin

0

Se non è possibile cambiare facilmente il JSON si sta lavorando, si può semplicemente implementare new sui modelli. Sto usando il seguente per ingannare il JSON nella forma prevista, che nel mio caso comporta l'eliminazione della chiave "id" e alcuni che non esistono nel mio modello locale. Ho implementato questo in application_record.rb e casi speciali all'interno di ogni modello.

def self.new(properties = {}) 
    super properties.select { |attr, _val| 
    (['id'] + attribute_types.keys).include?(attr.to_s) 
} 
Problemi correlati