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

kon*_*ung 5 json ruby-on-rails kendo-ui strong-parameters ruby-on-rails-4

背景

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

所以请致电:http://example.com/products/1.json - 返回json和http://example.com/products/1返回haml-parsed视图.

问题

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

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

第1部分

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

第2部分

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

Cam*_*way 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
Run Code Online (Sandbox Code Playgroud)

测试

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}
Run Code Online (Sandbox Code Playgroud)

要记住的一件事是参数可能不会真正以哈希形式出现,因此您可能必须调用 .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
Run Code Online (Sandbox Code Playgroud)

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

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