【问题标题】:Duplicate nested associations returned from json POST从 json POST 返回的重复嵌套关联
【发布时间】:2012-11-13 11:41:38
【问题描述】:

我正在尝试在我的 rails(版本 3.2.9)应用程序中使用 REST API。

我正在将一个 json 数据包发布到标准的 rails 脚手架控制器,其中包括模型本身的详细信息,以及嵌套的子节点。

父模型及其子模型已成功插入数据库。但是,来自 POST 的响应会使嵌套的子级加倍。

为什么 POST 响应中的嵌套关​​联会加倍?过去几天我一直在努力解决这个问题……这让我发疯了。

具体例子

JSON 请求

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 响应

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"
    }
}

型号

多对多模型

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

标签模型

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

音频事件模型

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

更新

根据 cmets 的要求,我在控制器中的操作代码

# 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

而且,我尝试删除stampable 和acts_as_paranoid 宏,但没有效果。

更新 2

如果我尝试添加 3 个嵌套属性,我会返回 6 个。 4 返还 8,5 返还 10。

【问题讨论】:

  • 我发布了关于 ̉̉as_json 的一种用途的答案:stackoverflow.com/questions/3872236/rails-object-to-hash/… 也许这可以帮助您找出问题所在。我将在今天晚些时候尝试实现您的代码,看看是否能找出发生了什么。
  • 好吧,我快速实现了您的模型,但似乎无法复制。它按预期工作。也许问题不在于您的模型,而在于您调用模型序列化的方式。你能用那个代码更新你的答案吗?
  • 我只是想知道,当您注释掉 stampable 和acts_as_paranoid 宏时,您是否尝试过看看会发生什么?也许他们会妨碍你。两者都在摆弄活动记录的内部......
  • 我很抱歉没有尽快回复 - 我本来希望收到电子邮件更新(我没有收到)。我在 cmets 中尝试了这两个建议,但没有运气。
  • 您确定audio_event_tags 在数据库中没有加倍吗?

标签: ruby-on-rails activerecord associations ruby-on-rails-3.2 has-many-through


【解决方案1】:

转自cmets。

看起来像 json 序列化问题。如果单独的 get 工作正常,您可以在控制器中的 format.json 之前尝试 @audio_event.audio_event_tags.reload

-- 这当然很奇怪。 audio_event_tags 的内存详细信息在 @audio_event.save 行中被弄乱了。 AudioEvent 是否有 after_save 回调?如果没有回调,那么我不知道这是怎么发生的。需要重现它。如果所有嵌套属性都发生这种情况,这是一个严重的问题。

【讨论】:

  • 这似乎是唯一有效的方法。我必须额外查询才能重新加载audio_event_tags,这有点烦人......而且我不明白为什么它需要这样......至少我可以继续前进。仅供参考@so_mv,AudioEvent 和 AudioEventTag 都有一个 after_save callack 集......从调试中,after_save 道具有一个数组,其中包含一个项目,属于同一类。我尝试清除这些数组无效。
  • 如果你想避免db调用,你可以试试@audio_event.audio_event_tags.uniq!{|aet| aet.tag_id} 而不是 .reload。请注意,uniq!仅适用于 Ruby 1.9.3。 v1.8.7 也有 uniq 但不使用块进行比较。
猜你喜欢
  • 2014-10-22
  • 2015-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-25
  • 2019-08-20
  • 2019-05-20
  • 2015-08-01
相关资源
最近更新 更多