简体   繁体   English

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

[英]Rails 4.2 strong params and parsing changed attributes on a json put / update

Background 背景

I have several models with about 40 attributes each. 我有几个模型,每个模型有40个属性。 They are normalized, it's just that the models we are dealing with have to have multiple attributes. 它们被标准化,只是我们正在处理的模型必须具有多个属性。 Other than that it's a pretty standard rails 4.2 app ( upgrading from rails 3.2) . 除此之外,它是一个非常标准的rails 4.2应用程序(从rails 3.2升级)。 The app is used both to serve dynamic pages sprinkled with ajax calls, and json so it can be used by various json clients. 该应用程序既可用于提供带有ajax调用的动态页面,也可用于json,因此可供各种json客户端使用。

So a call to : http://example.com/products/1.json - returns json and http://example.com/products/1 returns haml-parsed view. 所以请致电: http//example.com/products/1.json - 返回json和http://example.com/products/1返回haml-parsed视图。

Problem 问题

JavaScript library I'm using ( KendoUI) returns a whole record on update, not just fields that got updated. 我正在使用的JavaScript库(KendoUI)返回更新的整个记录​​,而不仅仅是更新的字段。 Currently there is no way to avoid unless I want to rewrite KendoUi Grid according to their support forum. 目前没有办法避免,除非我想根据他们的支持论坛重写KendoUi Grid。

So user can search all the products, and I display all the attributes of the product to her, and then based on her access level she can update certain fields (several pricing points and description), however ajax request contains ALL the attributes. 因此,用户可以搜索所有产品,并向我显示产品的所有属性,然后根据她的访问级别,她可以更新某些字段(几个定价点和描述),但是ajax请求包含所有属性。 So as a result I get ActiveModel::ForbiddenAttributesError 因此,我得到了ActiveModel :: ForbiddenAttributesError

Question

Part 1 第1部分

How can I properly ( the rails way so to speak) filter out params that got updated? 我怎样才能正确(可以这么说的轨道)过滤掉更新后的参数? Right now I'm doing a hash comparison of @product.attributes && params[:product] to figure out if there are updated/ new attributes passed. 现在我正在对@ product.attributes && params [:product]进行哈希比较,以确定是否传递了更新/新属性。 .diff was deprecated as of Rails 4.0.2 .diff自Rails 4.0.2起已被弃用

Part 2 第2部分

My admins are allowed to change almost all attributes on these big models ( except timestamps, id, and a few others). 我的管理员可以更改这些大型模型上的几乎所有属性(时间戳,ID和其他一些除外)。 What is the best way to do it rather than doing params[:product].require(:base_cost).permit(:vendor_cost, :client_cost) for 30 odd attributes? 最好的方法是做什么而不是做params [:product] .require(:base_cost).permit(:vendor_cost,:client_cost)30个奇数属性? It quickly becomes a problem to maintain these lists if the app is in development and attributes change. 如果应用程序处于开发状态并且属性发生变化,则很快就会成为维护这些列表的问题。 I guess I could use some kind of CONSTANT - ALLOWED_ATTRIBUTES or ADMIN_ATTRIBUTES and USER_ATTRIBUTES, and pass that to permit. 我想我可以使用某种CONSTANT - ALLOWED_ATTRIBUTES或ADMIN_ATTRIBUTES和USER_ATTRIBUTES,并将其传递给permit。 But kind of feels un-Railsy? 但有点感觉不Railsy?

Part 1 第1部分

Even if the function is deprecated you can still use this functionality. 即使该函数已弃用,您仍然可以使用此功能。 With what your trying to do a few might be useful here. 根据您的尝试,这里可能会有一些用处。

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

Tests 测试

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}

One thing to keep in mind is the params may not truly come in as a hash, so you may have to call .to_hash 要记住的一件事是params可能不是真正的哈希值,所以你可能不得不打电话.to_hash

Part 2 第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

You could add defaults to this pretty easily and some other nifty pieces of functionality but at least as a start this should do it for you. 你可以很容易地添加默认值和一些其他漂亮的功能,但至少作为一个开始,这应该为你做。

Please keep in mind not all this has been thoroughly tested, there could be bugs! 请记住,并非所有这些都经过彻底测试,可能存在错误!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM