Pregunta Creación de objetos anidados con JSON en Rails


¿Cómo paso JSON a una aplicación RAILS para que cree objetos secundarios anidados en una relación has_many?

Esto es lo que tengo hasta ahora:

Dos objetos 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 Commute, 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 probando 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 eso, obtengo esta salida:

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 que las ubicaciones en las que se está leyendo 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 venir de cualquier lado.

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.


Mientras 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

32
2018-05-10 13:51


origen


Respuestas:


Lo descubrió, el objeto de ubicaciones debería haberse llamado localidades_attributes para coincidir con el esquema de nombres de creación de objetos anidados de Rails. 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"
            }
        ]
    }
}

36
2018-05-12 23:58



Si no puede cambiar fácilmente el JSON con el que está trabajando, puede implementar new en tus 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) 
}

0
2018-01-01 21:42