2009-05-10 624 views
30

¿Cómo transfiero JSON a una aplicación RAILS para que cree objetos secundarios anidados en una relación has_many?Creación de objetos anidados con JSON en Rails

Esto es lo que tengo hasta ahora:

Dos objetos del modelo.

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 Conmutar, tengo un controlador estándar configurado. Me gustaría poder crear un objeto Commute así como varios objetos de ubicación secundarios en una sola llamada REST usando JSON. He estado tratando de cosas como esta:

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 más legible, el JSON es:

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

Cuando ejecuto esto, me da este resultado:

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' 

Parece como las ubicaciones en las que se lee la matriz JSON, pero no se interpreta como un objeto Ubicación.

Puedo cambiar fácilmente el cliente o el servidor, por lo que la solución podría provenir de cualquiera de los lados.

Entonces, ¿RAILS me facilita esto? ¿O necesito agregar algún soporte para esto a mi objeto Commute? Tal vez agregar un método from_json?

Gracias por cualquier ayuda.


Como he estado trabajando en esto, una solución que funciona es modificar mi controlador. Pero esta no parece ser la forma de "dardos" para hacerlo, así que por favor díganme si hay una manera mejor.

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 

Respuesta

35

lo descubrió, los lugares objeto debe haber sido llamado locations_attributes para que coincida con los rieles anidados creación de objetos esquema de nombres. Después de hacer eso, funciona perfectamente con un controlador Rails predeterminado.

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

Parece que puede controlar la fuente JSON, pero ¿qué pasa si no puede, cómo manejaría "ubicaciones"? – Jerome

+0

Echa un vistazo, acabo de publicar una edición de una respuesta que se eliminó. –

+0

Para cualquiera que todavía tenga problemas con esto, debe permitir manualmente los atributos en su controlador. Ejemplo: 'params.require (: conmutar) .permit (: minutos,: startTime, locations_attributes: [: latitude,: longitude])' – Kevin

0

Si no se puede cambiar fácilmente el JSON que está trabajando, sólo se puede aplicar new en sus modelos. Estoy usando lo siguiente para cambiar el json a la forma esperada, que en mi caso implica eliminar la clave 'id' y algunas que no existen en mi modelo local. Tengo esto implementado en application_record.rb y casos especiales dentro de cada modelo.

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