Подтвердить что ты не робот

Создание вложенных объектов с помощью JSON в Rails

Как передать JSON в приложение RAILS, чтобы он создал вложенные дочерние объекты в отношениях has_many?

Вот что я до сих пор:

Два объекта модели.

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

class Location < ActiveRecord::Base
  belongs_to :commute
end

С Commute у меня установлен стандартный контроллер. Я хотел бы иметь возможность создать объект Commute, а также несколько дочерних объектов Location в одном вызове REST с использованием JSON. Я пробовал такие вещи:

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

Или более читаемым, JSON:

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

Когда я его выполню, я получаю этот вывод:

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'

Похоже, что содержимое массива JSON считывается, но не интерпретируется как объект Location.

Я могу легко изменить либо клиент, либо сервер, поэтому решение может появиться с обеих сторон.

Итак, делает ли RAILS это легко для меня? Или мне нужно добавить некоторую поддержку для этого объекта Commute? Возможно, добавьте метод from_json?

Спасибо за любую помощь.


Как я уже работал, одним из решений является изменение моего контроллера. Но это не похоже на способ "рельсов", поэтому, пожалуйста, дайте мне знать, есть ли лучший способ.

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
4b9b3361

Ответ 1

Выяснив это, объект местоположений должен был быть назван location_attributes, чтобы соответствовать схеме именования создания вложенных объектов Rails. После этого он отлично работает с контроллером Rails по умолчанию.

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

Ответ 2

Если вы не можете легко изменить json, с которым работаете, вы можете просто реализовать new на своих моделях. Я использую следующее, чтобы превзойти json в ожидаемой форме, что в моем случае включает в себя удаление ключа "id" и нескольких, которых нет в моей локальной модели. У меня это реализовано в application_record.rb и особых случаях в каждой модели.

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