Связи магистрали и Rails: предотвращение ошибок JSON HashWithIndifferentAccess - PullRequest
4 голосов
/ 03 января 2012

Я пытаюсь заставить свои базовые ассоциации работать в приложении rails, и у меня возникают трудности при обновлении существующих моделей. В частности, Rails выдает следующую ошибку:

Начато PUT "/ posts / 2" для 127.0.0.1 в 2012-01-04 02:36:14 + 1000
Обработка PostsController # update как JSON Параметры: { "Пост" => { "содержание" => "Seconderona", "creat_at" => "2012-01-03T10: 51: 09Z", "id" => 2, "title" => "Второй тест post "," updated_at "=>" 2012-01-03T10: 51: 09Z "," comments "=> [{}]}, "id" => "2"} Post Load (0.2ms) ВЫБЕРИТЕ "posts". * FROM "posts" WHERE "posts". "id" =? LIMIT 1 [["id", "2"]] ПРЕДУПРЕЖДЕНИЕ: нельзя назначить по массе Защищенные атрибуты: идентификатор завершен 500 Внутренняя ошибка сервера в 15 мс

ActiveRecord :: AssociationTypeMismatch (Комментарий (# 70104367824560) ожидал, получил ActiveSupport :: HashWithIndifferentAccess (# 70104367278120)):
app / controllers / posts_controller.rb: 62: in block in update'<br> app/controllers/posts_controller.rb:61:in update '

Несколько вещей:

Это срабатывает (например):

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

Кроме того, да, в модели присутствует 'accepts_nested_attributes_for'.

(Оскорбительный) код, приведенный ниже, взят в значительной степени дословно из книги thougtbot «Магистраль на рельсах», и я также попробовал следовать документации по гемам с реляционными магистралями. Оба поднимают эту ошибку. Любые идеи приветствуются, код ниже

МОДЕЛЬ "ПОЧТА" ЖЕЛЕЗНОДОРОЖНЫХ

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

МОДЕЛЬ "КОММЕНТАРИИ" ЖЕЛЕЗНОДОРОЖНЫХ

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

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'

Ответы [ 2 ]

26 голосов
/ 04 января 2012

Я недавно столкнулся с той же проблемой.На самом деле это не ошибка HashWithIndifferentAccess: это связано с тем, как accepts_nested_attributes_for ожидает параметры.

Когда вы объявляете accepts_nested_attributes_for :comments, Rails ищет вызов параметра comments_attributes для входящих параметров.

Проблема в том, что ваше представление JSON, полученное из Backbone, имеет свойство "comments" вместо свойства "comments_attributes".

Вы можете исправить это на стороне Backbone, добавив функцию toJSON в вашу модель Post:

# 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

Надеюсь, это поможет.

0 голосов
/ 23 сентября 2014

Для тех, кто гуглит ...

Несмотря на то, что текущий принятый ответ, безусловно, работает, следующее было в том, как я решил свою проблему (очень похожую, но немного другую):использовала вложенную форму , с вложенными моделями глубиной 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 переименовал параметры для меня, потому что я сообщил об этом заранее.

Надеюсь, это поможет!

...