Rails 4.2 强参数和解析 json put/update 上的更改属性

Posted

技术标签:

【中文标题】Rails 4.2 强参数和解析 json put/update 上的更改属性【英文标题】:Rails 4.2 strong params and parsing changed attributes on a json put / update 【发布时间】:2014-12-11 02:56:06 【问题描述】:

背景

我有几个模型,每个模型大约有 40 个属性。它们是标准化的,只是我们正在处理的模型必须具有多个属性。除此之外,它是一个非常标准的 rails 4.2 应用程序(从 rails 3.2 升级)。该应用程序既用于提供带有 ajax 调用的动态页面,也用于提供 json,因此它可以被各种 json 客户端使用。

所以调用:http://example.com/products/1.json - 返回 json 并且 http://example.com/products/1 返回经过haml解析的视图。

问题

我正在使用的 javascript 库 (KendoUI) 在更新时返回整条记录,而不仅仅是更新的字段。目前没有办法避免,除非我想根据他们的支持论坛重写 KendoUi Grid。

所以用户可以搜索所有产品,我向她显示产品的所有属性,然后根据她的访问级别,她可以更新某些字段(几个定价点和描述),但是 ajax 请求包含所有属性.因此,我得到 ActiveModel::ForbiddenAttributesError

问题

第 1 部分

我怎样才能正确地(可以说是 rails 方式)过滤掉更新的参数?现在我正在对@product.attributes && params[:product] 进行哈希比较,以确定是否传递了更新/新属性。 .diff 自 Rails 4.0.2 起已弃用

第 2 部分

我的管理员可以更改这些大型模型的几乎所有属性(时间戳、id 和其他一些属性除外)。最好的方法是什么,而不是为 30 个奇怪的属性做 params[:product].require(:base_cost).permit(:vendor_cost, :client_cost) ?如果应用程序正在开发并且属性发生变化,那么维护这些列表很快就会成为一个问题。我想我可以使用某种 CONSTANT - ALLOWED_ATTRIBUTES 或 ADMIN_ATTRIBUTES 和 USER_ATTRIBUTES,并将其传递给许可。但是感觉有点不像Railsy?

【问题讨论】:

【参考方案1】:

第 1 部分

即使该功能已被弃用,您仍然可以使用该功能。您尝试做的一些可能在这里有用。

class Hash
  def diff h2
    self.dup.delete_if  |k, v| h2[k] == v .merge(h2.dup.delete_if  |k, v| self.has_key?(k) )
  end

  def only *args
    h = 
    args.each do |a|
      h[a] = self[a]
    end
    h
  end

  def except *args
    h = 
    (self.keys - args).each do |a|
      h[a] = self[a]
    end
    h
  end
end

测试

2.0.0-p247 :001 > h1 = a: 1, b: 2, c: 3 #=> :a=>1, :b=>2, :c=>3 
2.0.0-p247 :002 > h2 = a: 3, b: 2, c: 1 #=> :a=>3, :b=>2, :c=>1 
2.0.0-p247 :003 > h1.except(:a) #=> :b=>2, :c=>3 
2.0.0-p247 :004 > h1.except(:c) #=> :a=>1, :b=>2 
2.0.0-p247 :005 > h1.except(:c, :a) #=> :b=>2 
2.0.0-p247 :006 > h1.only(:c, :a) #=> :c=>3, :a=>1

要记住的一点是,参数可能不会真正以哈希的形式出现,因此您可能必须调用 .to_hash

第 2 部分

class FooBar < ActiveRecord::Base
  #this controls access restriction, assuming you keep this setup:
  #delete this part and all fields are update-able (leave it undefined)
  #no fields can be updated if UPDATABLE_FIELDS[type].nil? or UPDATABLE_FIELDS[type][0].nil?
  UPDATABLE_FIELDS = 
    admin: [:except, :id, :timestamp], 
    user: [:only, :name]
  
end

class ActiveRecord::Base
  def fields_for_user_type type
    return true unless defined?(UPDATABLE_FIELDS)
    return nil if !UPDATABLE_FIELDS[type] || !UPDATABLE_FIELDS[type].first
    return UPDATABLE_FIELDS[type].first, UPDATABLE_FIELDS[type][1..-1]
  end
end

class ApplicationController < ActionController::Base
  def filter_params data, cls
    method, restriction = cls.fields_for_user_type(current_user.type)
    return data if method === true
    return  if method.nil?
    return data.except(restriction) if method == :except
    return data.only(restriction) if method == :only
  end
end

class FunController < ApplicationController
  def update
    record = FooBar.find(params[:id])
    record.update(filter_params(params[:data], FooBar))
  end
end

您可以很容易地为此添加默认值以及其他一些漂亮的功能,但至少作为开始,这应该为您完成。

请记住,并非所有这些都经过彻底测试,可能存在错误!

【讨论】:

以上是关于Rails 4.2 强参数和解析 json put/update 上的更改属性的主要内容,如果未能解决你的问题,请参考以下文章

AFNetworking 使用 Rails 进行 PUT 和删除

Rails 嵌套强参数,如何使用?

Rails:Webpacker 4.2 在 /app/public/packs/manifest.json heroku 中找不到应用程序

创建动作 - Rails

Rails 4.2 - url helper 引发“ArgumentError:错误数量的参数(3 for 1..2)”

rails - 设计强参数总是不允许注册