Создание вложенных объектов с помощью JSON в Rails - PullRequest
32 голосов
/ 10 мая 2009

Как передать 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

Ответы [ 2 ]

36 голосов
/ 13 мая 2009

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

{
    "commute": {
        "minutes": 0,
        "startTime": "Wed May 06 22:14:12 EDT 2009",
        "locations_attributes": [
            {
                "latitude": "40.4220061",
                "longitude": "40.4220061"
            }
        ]
    }
}
0 голосов
/ 02 января 2017

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

 def self.new(properties = {})
  super properties.select { |attr, _val| 
    (['id'] + attribute_types.keys).include?(attr.to_s) 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...