Backbone 和 Rails 关联:避免 JSON HashWithIn DifferentAccess 错误

发布于 2024-12-24 01:26:09 字数 2626 浏览 3 评论 0原文

我正在尝试让我的骨干关联在 Rails 应用程序中工作,并且在尝试更新现有模型时遇到困难。具体来说,Rails 会抛出以下错误:

于 2012-01-04 02:36:14 +1000 开始对 127.0.0.1 放置“/posts/2”
由 PostsController#update 作为 JSON 参数处理: {"post"=>{"content"=>"Seconderona", "created_at"=>"2012-01-03T10:51:09Z", "id"=>2, "title"=>"第二次测试 帖子", "updated_at"=>"2012-01-03T10:51:09Z", "评论"=>[{}]}, "id"=>"2"} 加载后 (0.2ms) SELECT "posts".* FROM "posts" WHERE “帖子”.“id”=? LIMIT 1 [["id", "2"]] 警告:无法批量分配 受保护的属性:id 在 15 毫秒内完成 500 内部服务器错误

ActiveRecord::AssociationTypeMismatch(注释(#70104367824560) 预期的,得到的 ActiveSupport::HashWithIn DifferentAccess(#70104367278120)):
app/controllers/posts_controller.rb:62:in 更新中的块'
app/controllers/posts_controller.rb:61:in
更新'

一些事情:

这是触发的(例如):

c = window.router.comments.models[0]
c.save({content: 'Changed content'})

另外,是的,模型中存在“accepts_nested_attributes_for”。

下面的(有问题的)代码几乎逐字取自thoughtbot的“backbone on Rails”电子书,我也尝试遵循backbone-relational gem的文档。两者都会引发此错误。任何想法表示赞赏,代码如下

RAILS 'POST' MODEL

class Post < ActiveRecord::Base
  has_many :comments

  accepts_nested_attributes_for :comments

  def as_json(options = nil)
    super((options || {}).merge(include: { comments: { only: [content] } } ))
  end
end

RAILS 'COMMENT' MODEL

class Comment < ActiveRecord::Base
  belongs_to :post

  accepts_nested_attributes_for :post

  def as_json(options = nil)
    super((options || {}).merge(include: { post: { only: [:title, :content]}}))
  end
end

BACKBONE POST CONTROLLER

class Backbonerelationaldemo.Models.Post extends Backbone.Model
  paramRoot: 'post'

  initialize: () ->
    comments = new Backbonerelationaldemo.Collections.CommentsCollection
    comments.reset(@get('comments'))
    @setComments(comments)

  setComments: (comments) ->
    @comments = comments


class Backbonerelationaldemo.Collections.PostsCollection extends Backbone.Collection
  model: Backbonerelationaldemo.Models.Post
  url: '/posts'

BACKBONE COMMENTS CONTROLLER

class Backbonerelationaldemo.Models.Comment extends Backbone.Model
  paramRoot: 'comment'

  initialize: () ->
    if (@has('post')) 
      @setPost(new Backbonerelationaldemo.Models.Post(@get('post')))

  setPost: (post) ->
    @post = post

class Backbonerelationaldemo.Collections.CommentsCollection extends Backbone.Collection
  model: Backbonerelationaldemo.Models.Comment
  url: '/comments'

I'm trying to get my backbone associations working inside a rails app , and I'm having difficulty when trying to update existing models. Specifically, Rails throws the following error:

Started PUT "/posts/2" for 127.0.0.1 at 2012-01-04 02:36:14 +1000
Processing by PostsController#update as JSON Parameters:
{"post"=>{"content"=>"Seconderona",
"created_at"=>"2012-01-03T10:51:09Z", "id"=>2, "title"=>"Second test
post", "updated_at"=>"2012-01-03T10:51:09Z", "comments"=>[{}]},
"id"=>"2"} Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE
"posts"."id" = ? LIMIT 1 [["id", "2"]] WARNING: Can't mass-assign
protected attributes: id Completed 500 Internal Server Error in 15ms

ActiveRecord::AssociationTypeMismatch (Comment(#70104367824560)
expected, got
ActiveSupport::HashWithIndifferentAccess(#70104367278120)):
app/controllers/posts_controller.rb:62:in block in update'
app/controllers/posts_controller.rb:61:in
update'

A few things:

This is triggered on (for example):

c = window.router.comments.models[0]
c.save({content: 'Changed content'})

Also, yes, 'accepts_nested_attributes_for' is present in the model.

The (offending) code below is taken pretty much verbatim from thougtbot's "backbone on rails" ebook, and I've also tried following the documentation for the backbone-relational gem. Both raise this error. Any ideas appreciated, code below

RAILS 'POST' MODEL

class Post < ActiveRecord::Base
  has_many :comments

  accepts_nested_attributes_for :comments

  def as_json(options = nil)
    super((options || {}).merge(include: { comments: { only: [content] } } ))
  end
end

RAILS 'COMMENT' MODEL

class Comment < ActiveRecord::Base
  belongs_to :post

  accepts_nested_attributes_for :post

  def as_json(options = nil)
    super((options || {}).merge(include: { post: { only: [:title, :content]}}))
  end
end

BACKBONE POST CONTROLLER

class Backbonerelationaldemo.Models.Post extends Backbone.Model
  paramRoot: 'post'

  initialize: () ->
    comments = new Backbonerelationaldemo.Collections.CommentsCollection
    comments.reset(@get('comments'))
    @setComments(comments)

  setComments: (comments) ->
    @comments = comments


class Backbonerelationaldemo.Collections.PostsCollection extends Backbone.Collection
  model: Backbonerelationaldemo.Models.Post
  url: '/posts'

BACKBONE COMMENTS CONTROLLER

class Backbonerelationaldemo.Models.Comment extends Backbone.Model
  paramRoot: 'comment'

  initialize: () ->
    if (@has('post')) 
      @setPost(new Backbonerelationaldemo.Models.Post(@get('post')))

  setPost: (post) ->
    @post = post

class Backbonerelationaldemo.Collections.CommentsCollection extends Backbone.Collection
  model: Backbonerelationaldemo.Models.Comment
  url: '/comments'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

又爬满兰若 2024-12-31 01:26:09

我最近处理了同样的问题。它实际上不是 HashWithIn DifferentAccess 错误:它与 accepts_nested_attributes_for 期望参数的方式有关。

当您声明 accepts_nested_attributes_for :comments 时,Rails 会在传入参数上查找参数调用 comments_attributes

问题是来自 Backbone 的 JSON 表示具有 "comments" 属性,而不是 "comments_attributes" 属性。

您可以通过向 Post 模型添加 toJSON 函数来在 Backbone 端修复它:

# in your Post model
toJSON: ->
  attrs = _.clone(@attributes)
  attrs.comments_attributes = _.clone(@attributes.comments)
  delete attrs.comments
  attrs

或者您可以在 Rails 控制器中处理它:

# in your Posts controller
def update
  params[:comments_attributes] = params.delete(:comments) if params.has_key? :comments
  # call to update_attributes and whatever else you need to do
 end

希望这会有所帮助。

I dealt with the same issue recently. It's actually not a HashWithIndifferentAccess error: it has to do with how accepts_nested_attributes_for expects params.

When you declare accepts_nested_attributes_for :comments, Rails looks for a parameter call comments_attributes on the incoming params.

The problem is that your JSON representation coming from Backbone has a "comments" property instead of a "comments_attributes" property.

You could fix it on the Backbone side by adding a toJSON function to your Post model:

# in your Post model
toJSON: ->
  attrs = _.clone(@attributes)
  attrs.comments_attributes = _.clone(@attributes.comments)
  delete attrs.comments
  attrs

Or you could handle it in your Rails controller:

# in your Posts controller
def update
  params[:comments_attributes] = params.delete(:comments) if params.has_key? :comments
  # call to update_attributes and whatever else you need to do
 end

Hopefully this helps.

回首观望 2024-12-31 01:26:09

对于那些正在谷歌搜索的人...

虽然当前接受的答案确实有效,但以下是我解决问题的方法(非常相似,但略有不同):

我正在使用 嵌套表单,模型嵌套 4 层深。在渲染表单的控制器中,我需要构建表单。

def new
  @survey = Survey.new
  3.times do
    question = @survey.questions.build
    4.times { question.answers.build }
  end
end

这允许参数包含 params[:survey][:questions_attributes]。 Rails 帮我重命名了参数,因为我提前通知了它。

希望这有帮助!

For those who are Googling...

While the current accepted answer certainly works, the following was how I worked out my problem (very similar, but slightly different):

I was using a nested form, with models nested 4 layers deep. In my controller which was rendering the form, I needed to build out the form.

def new
  @survey = Survey.new
  3.times do
    question = @survey.questions.build
    4.times { question.answers.build }
  end
end

This allowed the params to include params[:survey][:questions_attributes]. Rails renamed the parameters for me because I informed it ahead of time.

Hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文