【问题标题】:Nested strong parameters in rails - AssociationTypeMismatch MYMODEL expected, got ActionController::Parameters()Rails 中的嵌套强参数 - 预期 AssociationTypeMismatch MYMODEL,得到 ActionController::Parameters()
【发布时间】:2014-01-31 21:19:39
【问题描述】:

我正在渲染一个模型,它是 JSON 格式的子书,如下所示:

{"id":2,"complete":false,"private":false, "books" [{ "id":2,"name":"Some Book"},.....

然后我通过将相同的 JSON 传回我的控制器来更新此模型,我收到以下错误:

ActiveRecord::AssociationTypeMismatch (Book (#2245089560) 预期,得到 ActionController::Parameters(#2153445460))

在我的控制器中,我使用以下内容进行更新:

@project.update_attributes!(project_params)

private

def project_params
    params.permit(:id, { books: [:id] } )
end

无论我在permit 中将哪些属性列入白名单,我似乎都无法保存子模型。

我是否遗漏了一些明显的东西?

更新 - 另一个例子:

控制器:

def create
    @model = Model.new(model_params)
end
def model_params
    params.fetch(:model, {}).permit(:child_model => [:name, :other])
end

请求:

post 'api.address/model', :model => { :child_model => { :name => "some name" } }

型号:

accepts_nested_attributes_for :child_model

错误:

预期的 ChildModel,得到 ActionController::Parameters

试过这个方法无效:http://www.rubyexperiments.com/using-strong-parameters-with-nested-forms/

【问题讨论】:

    标签: json ruby-on-rails-4 strong-parameters


    【解决方案1】:

    您是否在项目模型中使用accepts_nested_attributes_for :books?如果是这样,那么密钥应该是"books_attributes",而不是"books"

    def project_params
      params.permit(:id, :complete, :false, :private, books_attributes: [:id, :name])
    end
    

    【讨论】:

    • 谢谢 Josh - 我正在使用 accept_nested_attributes_for :books 但在更新对象时我没有使用嵌套形式,我使用的是 json 输出。当我将密钥更改为:books_attributes 时,它似乎没有更新书籍并将书籍指定为不允许的参数。
    • @AlanH,你需要编辑你的表单,像这样:f.fields_for :book, book do |b|
    • 感谢@fl00r,但我通过 AngularJS 传递数据并且没有表单。
    • @AlanH 你有没有找到解决方案?我面临着完全相同的问题。
    • @Duane - 不,我最终将模型分开发布,因为我找不到好的解决方案。我用另一个例子更新了这个问题。
    【解决方案2】:

    我正在使用 Angular.js & Rails & Rails 序列化程序,这对我有用:

    型号

    • has_many :功能
    • accepts_nested_attributes_for :features

    模型序列化器

    • has_many :features, root: :features_attributes

    控制器

    • params.permit features_attributes: [:id, :enabled]

    AngularJS

    • ng-repeat="feature in model.features_attributes track by feature.id

    【讨论】:

    • 什么是ModelSerializer?
    • @jerzy 我想他可能在谈论this
    【解决方案3】:

    我使用 ember.js 的解决方案是手动设置 books_attributes。

    在控制器中:

    def project_params      
      params[:project][:books_attributes] = params[:project][:books_or_whatever_name_relationships_have] if params[:project][:books_or_whatever_name_relationships_have]
      params.require(:project).permit(:attr1, :attr2,...., books_attributes: [:book_attr1, :book_attr2, ....])
    
    end
    

    因此,rails 会按预期检查和过滤嵌套属性

    【讨论】:

      【解决方案4】:

      这对我有用。我的父模型是Artist,子模型是Url

      class ArtistsController < ApplicationController
      
        def update
          artist = Artist.find(params[:id].to_i)
          artist.update_attributes(artist_params)
          render json: artist
        end
      
      private
      
        def artist_params
          remap_urls(params.permit(:name, :description, urls: [:id, :url, :title, :_destroy]))
        end
      
        def remap_urls(hash)
          urls = hash[:urls]
          return hash unless urls
          hash.reject{|k,v| k == 'urls' }.merge(:urls_attributes => urls)
        end
      end
      
      class Artist < ActiveRecord::Base
        has_many :urls, dependent: :destroy
        accepts_nested_attributes_for :urls, allow_destroy: true
      end
      
      class Url < ActiveRecord::Base
        belongs_to :artist
      end
      

      ...在咖啡脚本中(处理删除):

        @ArtistCtrl = ($scope, $routeParams, $location, API) ->
      
          $scope.destroyUrls = []
      
          $scope.update = (artist) ->
            artist.urls.push({id: id, _destroy: true}) for id in $scope.destroyUrls
            artist.$update(redirectToShow, artistError)
      
          $scope.deleteURL = (artist,url) ->
            artist.urls.splice(artist.urls.indexOf(url),1)
            $scope.destroyUrls.push(url.id)
      

      【讨论】:

        【解决方案5】:

        所有答案都缺少某些内容,即表单中fields_for 的输入。

        如果你这样做,表单会起作用:

        f.fields_for @model.submodel do ..
        

        但是,表单以model[submodel] 发送,但这就是其他人在回答中提到的错误的原因。如果你尝试使用model.update(model_params),Rails 将引发一个错误,它需要 Submodel 类型。

        要解决此问题,请确保遵循 :name, value 格式:

        f.fields_for :submodel, @model.submodel do ...
        

        然后在控制器中,确保将_attributes 放在参数上:

        def model_params
          params.require(:model).permit(submodel_attributes: [:field])
        end
        

        现在保存、更新等都可以正常工作了。

        【讨论】:

          【解决方案6】:

          浪费了几天时间试图弄清楚如何在 Angular 中使用 Accepts_nested_attributes,但问题总是一样的:Rails 白名单不允许变量进入 params 哈希。我已经尝试了每个人在 SO 和其他博客上所说的每种不同的白名单语法,尝试使用 :inverse,尝试使用 habtm 和 mas_many_through,尝试手动滚动我自己的解决方案,但如果白名单不允许参数通过,那将不起作用,尝试做什么http://guides.rubyonrails.org 谈到“在强参数范围之外”,尝试将白名单全部删除,这并不是一个真正的选择,但它还是会导致其他问题。不知道为什么rails 4强参数白名单不允许通过任意数据,这是一个巨大的问题,特别是如果accepts_nested_attributes也不起作用......我想我们只能在单独的页面/表单/控制器上创建/删除所有关联看起来像个白痴,让我的最终用户使用多个表单/页面来做一些应该在 1 个页面上用 1 个表单轻松完成的事情。你知道,通常我认为 Angular 会搞砸我,但这次 Angular 工作得很好,实际上是 Rails 4 在 1 个应该非常简单的问题上搞砸了我两次。

          【讨论】:

          • 这并不是直接回答问题。你能编辑你的帖子,让它直接回答发布的问题吗?
          • 我同意最后一条评论,但我对这个回复表示同情,因为从 Rails 4.2.0 开始,接受的答案似乎不起作用。我经历了类似的马戏团,在互联网上搜索解决方案并尝试每种可用的许可模式。我很幸运地设置了一个单独的 json 数组,它接受我解析出来的模型对象的属性,然后手动设置活动记录关联,但这使得客户端和服务器端代码都超级丑陋。
          猜你喜欢
          • 1970-01-01
          • 2021-03-02
          • 2017-07-01
          • 1970-01-01
          • 2015-09-20
          • 2019-12-27
          • 1970-01-01
          • 2014-10-20
          • 1970-01-01
          相关资源
          最近更新 更多