I have an API that returns info straight from the database and noticed Rails is storing a utf8 check mark and the authenticity token. What is the best way to remove this before it is saved?
{"achievements":[{"utf8":"✓","authenticity_token":"XDzrmNqNyt6OfhWpAzu3j7LhMd/sRcr/4oLsPxRMrDY=","achievement":"Big Guns",...
An example of the controller code:
def achievements
if params.include?(:ach)
#achievement = Achievement.new(params[:ach])
if #achievement.save
redirect_to action: :achievements and return
end
end
end
Example of the view:
<%= form_tag '', multipart: true do %>
<div class="four columns">Name <input name="ach[achievement]" type="text" ></div>
<div class="five columns">Description <input name="ach[description]" type="text" ></div>
<% end %>
Rather than try to write up a long post, I'll just note 3 things that should get you headed in the right direction. If it's confusing, comment and I'll explain further:
The utf8=✓ is inserted into your HTML when you use the form helpers in Rails.
You should not be handing the entire params object to your model.
You should always build your JSON API responses from known-safe attributes.
You can remove the key-pair from the params hash, by using in the reject method in the Hash class.
Example:
hash.reject! {|k,_| k.to_s.eql?("utf8") || k.to_s.eql?("authenticity_token") }
Dynamic fields must be enabled in the configuration.
As mentioned by coreyward, params should be selectively added to model, in this case.
This is what ended working best for me:
achievement_params = %w[achievement description]
#achievement = Achievement.new(params.select { |key,_| achievement_params.include? key })
Related
I think what I would like to achieve is rather straightforward. I would like to have a form with 5 fields, and a person can create any number of objects from that form (i.e. If you fill in only 1 field, you create 1 object, if you field 2 fields, you create 2 objects etc...)
Interestingly, nothing much comes up when I attempt to google this topic. The only "proper" example I came across was this. Which leads me to where I'm currently at (cause it doesn't work).
Here's my foo_controller.rb
def new
#foo = []
5.times do
#foo << Foo.new
end
end
def create
if params.has_key?("foos")
Foo.create(foo_params(params["foos"]))
else
params["foos"].each do |f|
Foo.create(foo_params(f))
end
end
end
private
def foo_params(my_params)
my_params.permit(:item_one, :item_two)
end
And here's the new.html.erb
<%= form_tag foos_path do %>
<% #foo.each do |f| %>
<% fields_for "foos[]", f do |ff| %>
#all the form stuff, labels, buttons etc
<% end %>
<% end %>
<% end %>
Now before I even hit submit, I already "know" that there's one problem. My form fields all have the same ID, which means even "if" I could submit without any problems, only the last entry would end up hitting the database. So...
Problem #1
- How do I have unique form IDs?
The next issue I face is when I hit submit, I get this error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params), so basically the params is not receiving the right thing. And that's my next problem...
Problem 2
- What's the proper way to be passing an Array of params?
But that is all assuming that THIS guide's solution works. I followed pretty much every step but came up empty. So i guess my big question/problem is:
How do i create One or Multiple objects of a single model?
Looks like you are headed in the right direction.
Problem 1: The IDs
You don't need IDs if you are creating new records. If you want to be able to edit the records with the same form it may be better to generate IDs, but that is more of a decision on how you want to handle create vs. update.
The way this works is Rails looks at the Name of the form elements. If you have empty brackets in the name Rails will render the params into an array:
<input type="text" name="foo[bar][][baz]" value="one" />
<input type="text" name="foo[bar][][baz]" value="two" />
<input type="text" name="foo[bar][][baz]" value="three" />
params => { foo: { bar: ["one", "two", "three"]}}
But, if you have IDs, Rails will render params into a hash:
<input type="text" name="foo[bar][1][baz]" value="one" />
<input type="text" name="foo[bar][2][baz]" value="two" />
<input type="text" name="foo[bar][3][baz]" value="three" />
params => { foo: { bar: {1: "one", 2: "two", 3: "three"}}}
Problem 3: Creating the models
Now the trick is how you handle the creation of the models. If you want to use nested attributes you can do that by setting up #accepts_nested_attributes_for and nesting everything under bar_attributes. This will let you just pass the params hash to the model and have it work some magic.
Alternatively you might want to iterate over the params hash and make each model right in your controller.
Or create a form object to contain all that logic. Up to you.
I would start with the manual iterate-over-params approach and get things working. Then you can refactor and clean things up.
This might look something like this in your controller:
array_of_bars = foo_params[:bar].map do |bar_params|
Bar.new(baz: bar_params[:baz])
end
Take a look at RailCast's Dynamic Forms and Nested Forms for more details. I revisit those guides numerous times over the last few years and have been happy with the resulting code every time.
Nested forms is always tricky but there are gems that can do that for you. One of my personal favourites would be this: https://github.com/nathanvda/cocoon
Here's a tutorial using the gem https://www.sitepoint.com/better-nested-attributes-in-rails-with-the-cocoon-gem/
I've been dealing with the same error Rack::QueryParser::ParameterTypeError (expected Array (got Rack::QueryParser::Params). This answer helped me to fix it.
The error is caused by the fact that the records with an ID render params into a hash. The solution: you don't need the fields_for. Removing the fields_for block and just using form tag helpers instead should work, such as:
text_field_tag "foos[][name]", f["name"]
With this approach, there will be no IDs that mess with your params. If you submit and inspect the params, you will see a nice array of hashes.
I've started playing a bit with the react-rails gem and am struggling to understand why I am unable to render a custom model attribute in react.js.
All database persisted attributes work fine, it's just custom getters that are returning nothing.
For the sake of simplicity, here's my User model with email, first_name and last_name as persisted attributes.
class User < ActiveRecord::Base
def full_name_and_email
"#{first_name} #{last_name} (#{email})"
end
end
When rendering my view, I initialize a react component:
<%= react_component 'Header', { data: current_user } %>
And within the Header component, if I try the following inside the render function:
var user = this.props.data;
...
return(
<p>Welcome {user.email}</p>
)
My rendered HTML is:
<p data-reactid=".0.0.2">
<span data-reactid=".0.0.2.0">Welcome </span>
<span data-reactid=".0.0.2.1">super_admin#email.com</span>
</p>
However if I try
render(
<p>Welcome {user.full_name_and_email}</p>
)
My rendered HTML results in
<p data-reactid=".0.0.2">
<span data-reactid=".0.0.2.0">Welcome </span>
</p>
I'm probably missing something pretty basic here so if anyone could please help me understand what's going on it would be great!
Thanks!
Answering my own question after re-reading the documentation for the gem:
props is either an object that responds to #to_json or an
already-stringified JSON object (eg, made with Jbuilder, see note
below).
And to add methods to to_json
current_user.to_json(methods: :full_name_and_email)
I have a variable that I want to pass with a form in rails.
The variable is not convenient because it is not part of the model. I really don't want to create a column in the DB just so I can pass the variable.
When I try to pass the variable using something innocuous like:
<input type="hidden" id="blah" value=<%= "#{#blah}" %> />
The hidden field turns up correctly in the source, but doesn't pass with the params hash. When I code it properly with something like id="review_blah" rails assumes it's part of the review model and gets angry because it isn't part of the model.
Isn't there some workaround?? Some way to pass a variable easily for these circumstances, without rebuilding your model?
Thanks
You need to define an attribute_accessor for the virtual attribute and you need to add it to the attr_accessible list so it can be used in form. Like this:
attr_accessor :blah
This makes the blah and blah= methods available which you can use to deal with the incoming data.
def blah=(data)
#do sth. with the submited data here
end
In this case you could access the data entered in the "blah" field as the local variable "data".
Then you need to make it a part of the regular form:
<%= f.hidden_field :blah, :value => <Your value here> %>
So the last thing you need to do is adding the field to the attr_accessible list.
attr_accessible :blah,....
Otherwise the request would fail because of mass assignment protection.
In model
attr_accessible :blah
Though it's very ugly you can do something like following
<input name='x' type="hidden" id="blah" value=<%= "#{#blah}" %> />
in your controller
#model.save
Model.update_all({:my_attribute => params['x']}, {:id => #model.id})
You need to set the name of the input parameter I think. otherwise it will not be contained in Rails params hash.
<input type="hidden" id="blah" value=<%= "#{#blah}" %> name="blah"/>
class ModelName
attr_accessor :blah
end
#model = ModelName.new
#model.blah = "blah blah"
#model.blah #will return "blah blah"
You don't need to have a column in your table. Check here
This way you can have
=form_for #model do |f|
=f.hidden_field :blah
This will pass blah along the object in the params hash.
Thanks to help from people earlier, I am getting a hang of how to serialize a nested hash into a single column. While I was able to generate the form fields at multiple levels and get values of the fields back in to a string, I am unable to retain the different levels for the hash.
My hash looks like the following:
My code looks like:
<% categoryvalue.each do |categoryproperty, categorypropertyvalue| %>
<div>
<%= f.fields_for :categories, categoryproperty do |categoryattribs| %>
<%= categoryattribs.label categoryproperty %> <br/>
<%= categoryattribs.text_field categoryproperty, :value => categorypropertyvalue %> <br/>
<% end %>
</div>
<% end %>
The final hash string in my example takes data for two categories and must look similar to the following string when it gets assigned to :categories should look like the following:
{"0" => {"Active"=>"yes", "totalproducts"=>"100", "segment"=>"Premium"}, "1" => "Active"=>"yes", "totalproducts"=>"190"}}
However, the string is coming in the following form:
{"Active"=>"yes", "totalproducts"=>"100", "segment"=>"Premium", "Active"=>"yes", "totalproducts"=>"190"}
Is there a way to differentiate the attributes of one category from another and have two separate hashes within the main hash? Right now everything gets flattened out to a single level. This is evident in how the id and names for input fields are generated. See the sample below:
<input id="product_categories_Active" name="product[categories][Active]" size="30" type="text" value="%" />
<input id="product_categories_Active" name="product[categories][Active]" size="30" type="text" value="lbs" />
What I actually want is product[categories][0][Active] and product[categories][1][Active].
Any suggestions on how to approach this?
Just in case someone stumbles on this question having a similar problem, I wanted to share the final solution.
You have to use text_field_tag, select_tag etc. instead of the fields_for helper. Use a loop (.each_with_index do |key, index|) to iterate through your hash. Then basically generate a fully qualified name for each field in the format "product_categories_0_active".
I will try to write a tutorial or blog on this once I am out of the woods on my project but I think most people will figure it out from here.
Hope it helps.
I've got a user model which contains a field called, lets say, text1. In the users/new form I want to have 4 individual text boxes for this text1. In my model I will take values from these boxes and concatenate them together as comma separated values and store them in the DB. To give you an understanding, this is what I want.
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
<input type="text" name="user[text1][]" />
How do I get this using form_for helper method? For now please don't worry yourself about the accessor method in the model, that is all taken care of. Thanks a ton.
Add few virtual attributes to your User model
class User < ActiveRecord::Base
attr_accessor :text1_part1
attr_accessor :text1_part2
attr_accessor :text1_part3
attr_accessor :text1_part4
def before_validation
self.text1 = "#{self.text1_part1}#{self.text1_part2}#{self.text1_part3}#{self.text1_part4}"
end
# make sure you fill the correct values for
# the text parts for an existing record.
# This can be done by implementing `after_find` or overriding
# `text1=`.
end
In your view code use the new attributes instead of text1
<% form_for(:user) do |f| %>
#some code
<%= f.text_field :text1_part1>
<%= f.text_field :text1_part2>
<%= f.text_field :text1_part3>
<%= f.text_field :text1_part4>
#some code
<%= f.submit "Save">
<% end %>
The previous answer gives a solution, so I am just providing some background as to why what you are asking for does not work they way you might hope.
You can indeed create a form with multiple input fields of the same name, and that data will be posted. However, when rails receives the post it automatically parameterizes the post data and/or url parameters. It essentially splits on the & and assigns the key/value pairs to the params hash. The outcome of this is that params[:user][:text1] (from your example) will have the value of the last instance of the user[text1] it encountered, since it is simply a value assignment to an existing key. You might want to dig into ActiveRecord multiparameter assignments to get an idea of how datetime attributes work, since they are similar to your use-case.
I am working on something similar and it sounds like maybe serialization is what you are looking for. Unfortunately I don't have my issues solved yet, so I can't provide anything more concrete.