I'm stuck with strong_parameters and this array of hashes with a dynamic hash (hstore) inside.
The structure is the following:
{ contact_sources: [
{ id: 1, filled_fields: { randomstuff: 'randomdata', dunno: 123 } },
{ id: 2, filled_fields: { blah: 'blabla', dunno: 9043 } }
] }
So, my main attempt is the following:
params.permit(contact_sources: [{:filled_fields => []}, 'id'])
Which doesn't return filled_fields. Any suggestion on how to deal with it?
Update 1:
I have the following model:
class ContactSource < ActiveRecord::Base
# Fields: id:integer, filled_fields:hstore
end
In my action, I'm submitting multiple records at once (mass update), so I have an array of contact_source, but actually they don't belong to anything, it's just a mass update.
Looks like it's not possible to do it with "plain" strong_parameters syntax. The only option you have is to actually, after filtering, re-add those values with a loop. I know it's terrible but it's the only way right now. I submitted a bug to Rails actually.
Related
I want to be able to support a set of parameters in either the root params object:
?foo=a
params.permit(:foo)
Or in a nested object:
?bar[foo]=a
params.require(:bar).permit(:foo)
I'm passing parameters into that controller from a form object, so the nested object happens naturally, but I'd like to also be able to pass the params in the root object in the query string and support both.
Is there an elegant, non-hacky way to accomplish this?
require is like [] with an exception when the value is missing (much like Hash#fetch). You can instead just use [] directly to get the nested structure if it's there, and fall back to the root params otherwise:
(params[:bar] || params).permit(:foo)
remember that require is different as permit.
The require method ensures that a specific parameter is present. is not necessary to use on nested ones, it makes sure that the required one come in the params or it will throw an error.
you can work with nested attributes like this
params = ActionController::Parameters.new({
person: {
name: "Francesco",
age: 22,
pets: [{
name: "Purplish",
category: "dogs"
}]
}
})
permitted = params.permit(person: [ :name, { pets: :name } ])
permitted.permitted? # => true
permitted[:person][:name] # => "Francesco"
permitted[:person][:age] # => nil
permitted[:person][:pets][0][:name] # => "Purplish"
permitted[:person][:pets][0][:category] # => nil
you can check the documentation on this here
The controller receives JSON object
{
user: {
name: "string",
details: {
info1: "string",
info2: []
}
}
}
During permission controller knows that can permit some defined fields - as name - and hash field details with all nested attributes - also with arrays. What is the correct solution for this situation?
BAD SOLUTIONS
permit cannot be used, because I must select user permitted fields
tap do |whitelisted| cannot be used, because it doesn't make that fields "permit"
case below cannot be user, because with arrays doesn't work
details_keys = params[:user][:details].keys
params.require(:user).permit(:name, details: details_keys)
If you want to permit a key having an array of permitted scalar values, then simply map the key to an empty array:
params.permit(key: [])
The permitted scalar types are String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile, and Rack::Test::UploadedFile
So when an array contains some non-scalar values like a hash, then you have to go further by permitting the nested keys too in the array.
Say, you have the following structure:
{
key: [
{
attr1: 'string',
attr2: 10
},
{
attr1: 'another string',
attr2: 100
}
]
}
then the permission goes in this way:
params.permit(key: [:attr1, :attr2])
Now let's assume your case looks like:
{
user: {
name: "sting",
details: {
info1: "string",
info2: [1, true, :sym] // assume it contains only permitted scalar values
}
}
}
the permission will be:
params.require(:user).permit(:name, details: [:info1, info2: []])
To automate this, lets assume details has 5 attributes with permitted scalar values and 3 more array attributes that also have only scalar values.
First pluck the 5 non-array keys of details:
non_array_keys = params[:user][:details].reject { |_, v| v.class == Array }.keys
Next the 3 array keys inside details:
array_keys = params[:user][:details].select { |_, v| v.class == Array }.keys.map { |k| { k => [] } }
Now the details_keys will be ready by:
details_keys = non_array_keys << array_keys
details_keys.flatten!
Final permission will look like:
params.require(:user).permit(:name, details: details_keys)
If the nested arrays would contain non-scalar values, then I guess you have got enough idea by this point on how to adapt to your changes!
DISCLAIMER: this automation is not appreciated because, instead of doing all these, a simple invocation of params.require(:user).permit! would suffice. But this marks the :user parameters hash and any sub-hash of it as permitted and does not check for permitted scalars, anything is accepted. Extreme care should be taken when using permit!, as it will allow all current and future model attributes to be mass-assigned.
For details, I would strongly suggest to look at the Rails official guide covering Strong Parameters in details.
Adding empty array might work. You can switch off validation for nested attributes?! Are you creating dynamic random input fields which you do not control?
Do you want to pass dynamic fields?
If that is the case, the following may work
Configuring strong parameters for dynamic keys
I'm building a rails API with a model containing an attribute data of JSON type. (PSQL)
But when I try to post something like this
{ model: { name: 'Hello', data: { a: 1, b: 2 } } }
Rails thinks a and b are the attributes of a nested data association... It considers then they are unpermitted params.
The thing is, { a: 1, b: 2 } is the value of my field data.
How to provide JSON value to an attribute ?
-
Edit:
The error displayed is:
Unpermitted parameters: name, provider, confidence, location_type, formatted_address, place_id, types, locality, ...
The value of the data attribute is { name: 'Name', provider: 'Provider', ... }
Like I said Rails thinks they are the attributes of a nested data association.
-
Log:
Pastebin
if the keys are unknown in advance this could be a workaround:
def model_params
data_keys = params[:model].try(:fetch, :data, {}).keys
params.require(:model).permit(data: data_keys)
end
Credit goes to aliibrahim, read the discussion https://github.com/rails/rails/issues/9454 (P.S seems like strong parameters will support this use case in Rails 5.1)
When you post something, you have to make sure that your json have the same parameters that your controller.
Example rails api:
def example
#model = Model.new(params)
#model.save
render(json: model.to_json, status: :ok)
end
def params
params.permit(:name, :provider, {:data => [:a, :b]})
end
Example front-end json for post:
var body = {
name: 'myName',
provider: 'provider',
data: {
a: 'something',
b: 'otherthing',
}
};
For some reason rails doesnt recognize a nested json, so you need to write into params.permit that data will be a json with that syntax, if where a array, the [] should be empty.
My model has a custom_fields column that serializes an array of hashes. Each of these hashes has a value attribute, which can be a hash, array, string, or fixnum. What could I do to permit this value attribute regardless of its type?
My current permitted params line looks something like:
params.require(:model_name).permit([
:field_one,
:field_two,
custom_fields: [:value]
])
Is there any way I can modify this to accept when value is an unknown type?
What you want can probably be done, but will take some work. Your best bet is this post: http://blog.trackets.com/2013/08/17/strong-parameters-by-example.html
This is not my work, but I have used the technique they outline in an app I wrote. The part you are looking for is at the end:
params = ActionController::Parameters.new(user: { username: "john", data: { foo: "bar" } })
# let's assume we can't do this because the data hash can contain any kind of data
params.require(:user).permit(:username, data: [ :foo ])
# we need to use the power of ruby to do this "by hand"
params.require(:user).permit(:username).tap do |whitelisted|
whitelisted[:data] = params[:user][:data]
end
# Unpermitted parameters: data
# => { "username" => "john", "data" => {"foo"=>"bar"} }
That blog post helped me understand params and I still refer to it when I need to brush up on the details.
I'm using the elasticsearch-rails gem and the elasticsearch-model gem and writing a query that happens to be really huge just because of the way the gem accepts queries.
The query itself isn't very long, but it's the filters that are very, very long, and I need to pass variables in to filter out the results correctly. Here is an example:
def search_for(input, question_id, tag_id)
query = {
:query => {
:filtered => {
:query => {
:match => {
:content => input
}
},
:filter => {
:bool => {
:must => [
{
# another nested bool with should
},
{
# another nested bool with must for question_id
},
{
# another nested bool with must for tag_id
}
]
}
}
}
}
}
User.search(query) # provided by elasticsearch-model gem
end
For brevity's sake, I've omitted the other nested bools, but as you can imagine, this can get quite long quite fast.
Does anyone have any ideas on how to store this? I was thinking of a yml file, but it seems wrong especially because I need to pass in question_id and tag_id. Any other ideas?
If anyone is familiar with those gems and knows whether the gem's search method accepts other formats, I'd like to know that, too. Looks to me that it just wants something that can turn into a hash.
I think using a method is fine. I would separate the searching from the query:
def query_for(input, question_id, tag_id)
query = {
:query => {
...
end
search query_for(input, question_id, tag_id)
Also, I see that this search functionality is in the User model, but I wonder if it is belongs there. Would it make more sense to have a Search or Query model?