I'm attempting use the REST API in my rails (version 3.2.9) app.
I am POSTing a json packet to a standard rails scaffolded controller, which includes details for the model itself, and nested children.
The parent model and its children are successfully inserted into the database. However, the response from the POST doubles the nested children.
Why are the nested associations being doubled in the POST response? I have been trying to get this right for the last few days... it is driving me nuts.
Specific examples
JSON request
POST http://localhost:3000/audio_events HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 432
Accept: application/json, text/plain, */*
{
"audio_event": {
"start_time_seconds": 0.05,
"end_time_seconds": 15.23,
"low_frequency_hertz": 1000,
"high_frequency_hertz": 8753,
"audio_event_tags_attributes": [
{
"tag_id": "-1"
},
{
"tag_id": "-2"
}
],
"audio_recording_id": "1bd0d668-1471-4396-adc3-09ccd8fe949a"
}
}
JSON response
HTTP/1.1 201 Created
Location: http://localhost:3000/audio_events/27
Content-Type: application/json; charset=utf-8
X-Ua-Compatible: IE=Edge
Etag: "c79ccdf981a9fadad3a8b08c3a878e8e"
Cache-Control: max-age=0, private, must-revalidate
Content-Length: 924
Server: WEBrick/1.3.1 (Ruby/1.9.3/2012-04-20)
Date: Mon, 26 Nov 2012 00:27:47 GMT
Connection: Keep-Alive
{
"created_at": "2012-11-26T00:27:32Z",
"creator_id": 1,
"deleted_at": null,
"deleter_id": null,
"end_time_seconds": "15.23",
"high_frequency_hertz": "8753.0",
"id": 27,
"is_reference": false,
"low_frequency_hertz": "1000.0",
"start_time_seconds": "0.05",
"updated_at": "2012-11-26T00:27:32Z",
"updater_id": 1,
"audio_event_tags": [
{
"audio_event_id": 27,
"created_at": "2012-11-26T00:27:32Z",
"creator_id": 1,
"tag_id": -1,
"updated_at": "2012-11-26T00:27:32Z",
"updater_id": 1
},
{
"audio_event_id": 27,
"created_at": "2012-11-26T00:27:32Z",
"creator_id": 1,
"tag_id": -2,
"updated_at": "2012-11-26T00:27:32Z",
"updater_id": 1
},
// DUPLICATES ARE HERE
{
"audio_event_id": 27,
"created_at": "2012-11-26T00:27:32Z",
"creator_id": 1,
"tag_id": -1,
"updated_at": "2012-11-26T00:27:32Z",
"updater_id": 1
},
{
"audio_event_id": 27,
"created_at": "2012-11-26T00:27:32Z",
"creator_id": 1,
"tag_id": -2,
"updated_at": "2012-11-26T00:27:32Z",
"updater_id": 1
}
],
"audio_recording": {
"id": 1,
"uuid": "1bd0d668-1471-4396-adc3-09ccd8fe949a"
}
}
Models
Many to many model
class AudioEventTag < ActiveRecord::Base
belongs_to :audio_event
belongs_to :tag
accepts_nested_attributes_for :audio_event
attr_accessible :audio_event, :tag, :tag_id
stampable
belongs_to :user, :class_name => 'User', :foreign_key => :creator_id
validates_uniqueness_of :audio_event_id, :scope => :tag_id
end
The tag model
class Tag < ActiveRecord::Base
has_many :audio_event_tags
has_many :audio_events, :through => :audio_event_tags
accepts_nested_attributes_for :audio_events, :audio_event_tags
attr_accessible :is_taxanomic, :text, :type_of_tag
stampable
belongs_to :user, :class_name => 'User', :foreign_key => :creator_id
acts_as_paranoid
validates_as_paranoid
end
The audio event model
class AudioEvent < ActiveRecord::Base
belongs_to :audio_recording
has_many :tags, :through => :audio_event_tags, :uniq => true
has_many :audio_event_tags
accepts_nested_attributes_for :audio_event_tags
attr_accessible :audio_recording_id, :end_time_seconds, :high_frequency_hertz, :is_reference,
:low_frequency_hertz, :start_time_seconds,
:tags_attributes, :audio_event_tags_attributes
stampable
belongs_to :user, :class_name => 'User', :foreign_key => :creator_id
acts_as_paranoid
validates_as_paranoid
# validation
validates :audio_recording, :presence => true
validates :start_time_seconds, :presence => true, :numericality => { :greater_than_or_equal_to => 0 }
validates :end_time_seconds, :numericality => { :greater_than_or_equal_to => 0 }
validates :low_frequency_hertz, :presence => true, :numericality => { :greater_than_or_equal_to => 0 }
validates :high_frequency_hertz, :numericality => { :greater_than_or_equal_to => 0 }
# json formatting
def as_json(options={})
super(
:include =>
[
:audio_event_tags,
:audio_recording => {:only => [:id, :uuid]}
],
:except => :audio_recording_id
)
end
end
Updates
As requested in comments, the code for my action in the controller
# POST /audio_events
# POST /audio_events.json
def create
@audio_event = AudioEvent.new(params[:audio_event])
#@audio_event.audio_event_tags.each{ |aet| aet.build() }
#@audio_event.audio_event_tags.count.times { @audio_event.audio_event_tags.build }
respond_to do |format|
if @audio_event.save
format.json { render json: @audio_event, status: :created, location: @audio_event }
else
format.json { render json: @audio_event.errors, status: :unprocessable_entity }
end
end
end
And, I tried removing the stampable and acts_as_paranoid macros to no effect.
Updates 2
If I try adding 3 nested attributes, I get 6 back. 4 gives back 8, and 5 gives back 10.