Backbone 和 Rails 关联:避免 JSON HashWithIn DifferentAccess 错误
我正在尝试让我的骨干关联在 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 15msActiveRecord::AssociationTypeMismatch (Comment(#70104367824560)
expected, got
ActiveSupport::HashWithIndifferentAccess(#70104367278120)):
app/controllers/posts_controller.rb:62:inblock in update'
update'
app/controllers/posts_controller.rb:61:in
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我最近处理了同样的问题。它实际上不是 HashWithIn DifferentAccess 错误:它与
accepts_nested_attributes_for
期望参数的方式有关。当您声明
accepts_nested_attributes_for :comments
时,Rails 会在传入参数上查找参数调用comments_attributes
。问题是来自 Backbone 的 JSON 表示具有
"comments"
属性,而不是"comments_attributes"
属性。您可以通过向 Post 模型添加 toJSON 函数来在 Backbone 端修复它:
或者您可以在 Rails 控制器中处理它:
希望这会有所帮助。
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 callcomments_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:
Or you could handle it in your Rails controller:
Hopefully this helps.
对于那些正在谷歌搜索的人...
虽然当前接受的答案确实有效,但以下是我解决问题的方法(非常相似,但略有不同):
我正在使用 嵌套表单,模型嵌套 4 层深。在渲染表单的控制器中,我需要构建表单。
这允许参数包含
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.
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!