Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 5 - how to whitelist the param for an ENTIRE jsonb postgres column in a controller?

I have tried to use the suggested example from github. You can also find it on some answers like this one for Rails 4.

I have tried this in Rails 5.0.1 and I just get an empty hash {} stored in the database:

def proposal_params
  params.require(:proposal).permit(:document, :account_id).tap do |whitelisted|
    whitelisted[:document] = params[:proposal][:document]
  end
end

Obviously if I just do permit! it works.

I also tried an answer from this question:

def proposal_params
  params.require(:proposal).permit(:account_id, document: Proposal.stored_attributes[:document])
end

but that also does not work.

The :document attribute contains json which is never the same between requests...and is a long and complex structure.

I just need to dump it as is into a jsonb column.

For the curious, here is an example of what can be in document:

{
   "document":{
      "customer":{
         "id":"273a0ad1-0867-4c17-8e0a-3284c6f2f6af",
         "first_name":"Ricardo",
         "last_name":"Bird",
         "email":"[email protected]",
         "mobile":"07786560223"
      },
      "state":8,
      "salutation":"Mr & Mrs Bird",
      "total_price":0,
      "quoted_products":[
         {
            "product":{
               "sku":"9111",
               "name":"Solid European Oak",
               "price":25.99,
               "category":"Wood",
               "sub_category":"Solid",
               "updated_at":"2016-12-01",
               "updated_by":"Donald Duck",
               "created_at":"2016-11-01",
               "created_by":"Mickey",
               "image_url":"http://www.higherground.co.uk/wp-content/uploads/2015/11/wood-flooring-thumbnail.jpg"
            },
            "total_price":25.99,
            "total_area":1,
            "product_total_price":25.99,
            "is_manual_total":false,
            "is_installed":false,
            "install_price":null,
            "are_rooms_grouped":false,
            "rooms":[
               {
                  "name":"Dining Room",
                  "icon_url":"assets/fb-img/dining-room.png",
                  "number":null,
                  "area":1,
                  "width":null,
                  "length":null,
                  "subfloor_prep":null,
                  "subfloor_price":null,
                  "perimeter_product":null,
                  "perimeter_length":null,
                  "is_perimeter_installed":false,
                  "perimeter_price":null,
                  "perimeter_style":null,
                  "is_perimeter_remove_old":false,
                  "is_move_furniture":false,
                  "move_furniture_price":null,
                  "move_surcharge":null,
                  "stairs_stepcount":null,
                  "surcharge":null,
                  "is_installed":false,
                  "uplift_price":null,
                  "install_method":"bonded"
               }
            ],
            "is_extras":true,
            "threshold_count":2,
            "radiator_count":3,
            "trim_count":2,
            "threshold_price":30,
            "radiator_price":4,
            "trim_price":10,
            "is_rear_mat":false,
            "is_front_mat":true,
            "front_mat_type":"Coloured",
            "rear_mat_type":null,
            "front_mat_area":2,
            "front_mat_price":60.01,
            "rear_mat_area":null,
            "rear_mat_price":null,
            "extras_total_price":212.01999999999998
         }
      ],
      "status":"Draft",
      "is_details_oneprice":false,
      "notes":"Testing submission"
   }
}
like image 607
rmcsharry Avatar asked Feb 02 '26 01:02

rmcsharry


2 Answers

If I'm correct you still need to permit! the document parameter:

def proposal_params
  params.require(:proposal).permit(:account_id).tap do |whitelisted|
    whitelisted[:document] = params[:proposal].fetch(:document, ActionController::Parameters.new).permit!
  end
end

The way this works is that it will only keep the account_id at first but then within tap we add the document parameter back by trying to retrieve it from the original parameters. ActionController::Parameters.new as the default value for fetch ensures that the permit! method is always callable even if no document parameter has been passed.

Under the hood ActionController::Parameters#permit! seems to recursively call the permit! function on the contained parameters as well, so we can call it on any instance:

def permit!
  each_pair do |key, value|
    Array.wrap(value).each do |v|
      v.permit! if v.respond_to? :permit!
    end
  end

  @permitted = true
  self
end

You can try:

to_unsafe_hash

See:

http://eileencodes.com/posts/actioncontroller-parameters-now-returns-an-object-instead-of-a-hash/

like image 29
Graham Slick Avatar answered Feb 03 '26 14:02

Graham Slick



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!