Rails 4 Checkboxes: Not getting saved to database - ruby-on-rails

I'm using Rails 4 and I have 3 models: property, category, and categorization.
The application is here: https://github.com/ornerymoose/PropertiesAndCategories
The idea: you can update a property and there's 6 categories (6 checkboxes) to choose from.
I came across this SO post and it was helpful but alas, my values still aren't getting saved.
When I click the update button to update a property: the server logs (if I select checkboxes 3 4 and 5) will be:
**Processing by PropertiesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Dcgy+3qBF4JLeu5MMATyqOc+jWyILXTfgURbqn+Ez8E2ru3rpzgPBqKDPWsy/QelQuQgOczdC8xDsOnnwSDAnQ==", "property"=>{"name"=>"Vizcaya", "category_id"=>["3", "4", "5", ""]}, "commit"=>"Update Property", "id"=>"6"}**
If I fire up rails console and check out the ^above property, category_id is set to nil. Why is this? My strong params are correct in the properties_controller I think:
def property_params
params.require(:property).permit(:name, :category_id => [])
end
Any and all input is greatly appreciated.

Why do you have ":category_id => []"? I'm pretty sure if you get rid of the "=> []" portion and just treat :category_id like :name (as I've shown below), it should work!
def property_params
params.require(:property).permit(:name, :category_id)
end

Ok, the first fix was here in the properties controller:
def property_params
params.require(:property).permit(:name, :category_ids => [])
end
I was initially using :category_id => [] which didn't return an error in the Rails server log, but wasn't saving the checkbox values to the database.
And then the second fix was fixing the collection_check_boxes arguments. Initially I had :category_id but I completely overlooked that it had to be :category_ids:
<%= collection_check_boxes(:property, :category_ids, Category.all, :id, :name)
Everything is working well now.

Related

ActiveAdmin passing variable in controller

I have a permit and vehicle model. I am trying to update the AA create controller to work how I have it in my rails app. That is taking the vehicle license_number entered and inputting it into the permit table, then also taking the inputted permit_id and inputting it into the permits attribute of the vehicle it is related to in the vehicle table.
admin/permit.rb
permit_params :permit_id, :vehicle, :date_issued, :issued_by, :date_entered, :entered_by
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, :collection => Vehicle.all.map{ |vehicle| [vehicle.license_number]}
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
controller do
def new
#permit = Permit.new
#vehicle = #permit.build_vehicle
#vehicle = Vehicle.all
super
end
def create
vehicle = Vehicle.find_by(license_number: permit_params[:vehicle_attributes][:license_number])
#permit = current_user.permit.build(permit_params.merge(date_entered: Date.today,
entered_by: current_user_admin.email))
super
end
end
My errors that I am getting, is that it is inputting the license_number in for the permit_id and then it is also saying the permit_params is not a defined variable. Any help would be great, thanks!
You have an interesting case here: it is confusing because you have a model called Permit, and usually in Rails you name the params method something like permit_params. Turns out, permit_params is the general method signature for ActiveRecord to implement strong params: https://activeadmin.info/2-resource-customization.html
With that, instead of calling permit_params in your create action, you need to call permitted_params[:vehicle_attributes][:license_number]. That’s why it’s considering permit_params as an undefined variable. Again, those two method signatures will be the same for all your ActiveAdmin forms.
Additionally, I’m not sure if this is a typo but you define #vehicle twice in your new method. I’m not sure you need to build a vehicle for the permit form unless you’re doing nested forms. Either way, I think the last line should read #vehicles = Vehicle.all Then you can use that in your form, which also could use an update in the collection part of your form field:
form do |f|
f.inputs do
f.input :permit_id
f.input :vehicle, collection: #vehicles.map { |vehicle| [vehicle.license_number, vehicle.id] }
f.input :date_issued, as: :date_picker
f.input :issued_by
end
f.actions
end
The collection_select form tag will take the first item in the array as the value that appears in the form, and the second value will be the value passed through in the params (https://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select).
Then in your create action, you can find the Vehicle with the id:
Vehicle.find(permitted_params[:vehicle_attributes][:vehicle_id])
I would avoid Permit as a model name, try using VehiclePermit.

How do you make a rails multi select dropdown work

I am trying to make a multi select dropdown work with a dialog for search parameters. I can make the dropdown multi select but can't seem to get/pass the resulting data. (edited/new info will be in italics)
I believe that the root of the problem is that I need to change the permit section in my controller to reflect that I am passing a hash/array. If I look at the resulting record, the 2 fields that I am setting as multi-selects show as nil. However, if I force an errror, the parameters shown by rails show the correct choices. therefore, I believe that the problem might be with the permit section.
That looks like
*def search_params
params.require(:search).permit(:document_title,
:summary,
:owner,
:category,
:file_name,
:doc_to_email,
:categories_attributes => [:name])
end*
I added the :categories_attributes => [:name] to try to get the controller to allow hashes but that didn't work.
The select field is
<%= f.select :category[], options_for_select(#categories.sort), {:include_blank => true}, {:multiple => true, :size =>10} %>
but that gives me
.erb where line #41 raised:
wrong number of arguments (0 for 1..2) Trace of template inclusion:
app/views/searches/new.html.erb
I thought I had to set category as an array with the [] but obviously I'm missing something.
Category is a string field in the Searches table.
You do not need the [] brackets after the field name as Rails adds those in automatically.
See the example here:
http://apidock.com/rails/ActionView/Helpers/FormTagHelper/select_tag
select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
# => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
# <option>Green</option><option>Blue</option></select>
In your case the selected values will be available as an array in params[:search][:category] after the form is submitted.
If you use strong parameters, also make sure you have :category => [] in the permit list.

Rails 4 acts_as_taggable_on :tag_list => [] strong param not working?

I am trying to use tagging on my notes model through this gem. However, even though I have explicitly added (2 seperate ways) :tag_list => [] to my strong params of my notes controller when ever I try and submit them, I still get an unpermitted parameter error in the logs? I have ran bundle install, and migrate as well.
Here are my files:
#/models/note.rb
class Note < ActiveRecord::Base
belongs_to :user
acts_as_taggable
validates_presence_of :name, :note_text, :note_style, :note_description
end
#/controllers/notes_controller.rb
.
.
.
def note_params
params.require(:note).permit(:name, :note_style, :note_text, :note_description, :tag_list => [])
end
and my notes form:
.form-group
= f.label :tag_list, "Tags (seperate by comma)"
= f.text_field :tag_list, class: 'form-control'
I followed everything from the gem but I still can't get it to work.
I actually got it to work by making my strong params by adding just :tag_list. Any idea why this works and not how they specified to do it in the gem documentation?
#/controllers/notes_controller.rb
.
.
.
def note_params
params.require(:note).permit(:name, :note_style, :note_text, :note_description, :tag_list, :tag_list => [])
end
The acts-as-taggable-on gem uses polymorphic association. In your case param as an empty array could not be initialized which might have caused the problem. Hope this clears your confusion. :-)
:tag_list => [] is needed when you're using a select tag in your form because a select tag returns an array when the form is submitted.
Since you are using a text field instead of a select tag, you are not returning an array when the form is submitted but a single value (the string in the text field), so you only need :tag_list in your permit parameters list.
Try using this in the list of permitted params
:tag_list => [:name, :taggings_count, :count]

Rails 4.0 Strong Parameters nested attributes with a key that points to a hash

I was playing around with Rails 4.x beta and trying to get nested attributes working with carrierwave. Not sure if what I'm doing is the right direction. After searching around, and then eventually looking at the rails source and strong parameters I found the below notes.
# Note that if you use +permit+ in a key that points to a hash,
# it won't allow all the hash. You also need to specify which
# attributes inside the hash should be whitelisted.
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb
So its saying you have to specify every single every single attribute within the has, I tried the following:
Param's example:
{"utf8"=>"✓",
"authenticity_token"=>"Tm54+v9DYdBtWJ7qPERWzdEBkWnDQfuAQrfT9UE8VD=",
"screenshot"=>{
"title"=>"afs",
"assets_attributes"=>{
"0"=>{
"filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
#tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
#original_filename="EK000005.JPG",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
}
}
},
"commit"=>"Create Screenshot"}
Controller
def screenshot_params
params.require(:screenshot).permit(:title,
:assets_attributes => [:filename => [:#tempfile,:#original_filename,:#content_type,:#headers]
The above isn't "working" (its not triggering carrierwave) however I am no longer getting errors (Unpermitted parameters: filename) when using the standard nested examples I found ex:
def screenshot_params
params.require(:screenshot).permit(:title, assets_attributes: :filename)
If anyone could help it would be great. I was not able to find a example with nested with a key that points to a hash.
My other answer was mostly wrong - new answer.
in your params hash, :filename is not associated with another hash, it is associated with an ActiveDispatch::Http::UploadedFile object. Your last code line:
def screenshot_params
params.require(:screenshot).permit(:title, assets_attributes: :filename)
is actually correct, however, the filename attribute is not being allowed since it is not one of the permitted scalar types. If you open up a console, and initialize a params object in this shape:
params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: 'a string'}}}
and then run it against your last line:
p = params.require(:screenshot).permit(:title, assets_attributes: :filename)
# => {"title" => "afa", "assets_attributes"=>{"0"=>{"filename"=>"abc"}}}
However, if you do the same against a params hash with the uploaded file, you get
upload = ActionDispatch::Http::UplaodedFile.new tempfile: StringIO.new("abc"), filename: "abc"
params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: upload}}}
p = params.require(:screenshot).permit(:title, assets_attributes: :filename)
# => {"title" => "afa", "assets_attributes"=>{"0"=>{}}}
So, it is probably worth a bug or pull request to Rails, and in the meantime, you will have to directly access the filename parameter using the raw params object:
params[:screenshot][:assets_attributes]["0"][:filename]
So, you're dealing with has_many forms and strong parameters.
This is the part of the params hash that matters:
"assets_attributes"=>{
"0"=>{
"filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
#tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
#original_filename="EK000005.JPG",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
}
}
when you define strong parameters like this...
permit(:assets_attributes => [:filename])
Things break, because where rails expects a filename it's getting this "0"
What does that number mean? It's the id for the asset you are submitting via your form. Now initially you might think you have to do something like
permit(:assets_attributes => [:id => [:filename]])
This looks like it follows other strong parameters syntax conventions. However, for better or for worse, they have made things a little easier, and all you have to write is:
permit(:assets_attributes => [:asset_id, :filename])
Edit -
As jpwynn pointed out in the comments, in Rails 4.2.4+ the correct syntax is
permit(:assets_attributes => [:id, :filename])
and that should work.
When you hit walls with strong params, the best thing to do is throw a debugger in your controller and test things out. params.require(:something).permit(:other_things) is just a method chain so you can try out different things on the full params hash until you find what works.
try
def screenshot_params
params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
end
I had this issue about a month ago and some searching around dug up this solution. It was adding the :id or :screenshot_id that fixed the problem (or both, I can't remember). This works in my code though.
Actually there is a way to just white-list all nested parameters.
params.require(:screenshot).permit(:title).tap do |whitelisted|
whitelisted[:assets_attributes ] = params[:screenshot][:assets_attributes ]
end
This method has advantage over other solutions. It allows to permit deep-nested parameters.
While other solutions like:
params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
Don't.
Source:
https://github.com/rails/rails/issues/9454#issuecomment-14167664
I had same problem just got it fixed now all you have to do is
params.require(:vehicle).permit(:user_id, assets_attributes: [:id, :image]).
Use pry gem to see what kind of attributes your asset object has makes sure theres an id and add other missing attribute, that should then work perfectly.
Am using paperclip assets is my nested object inside the vehicle class and an attachment of images is added to the asset.
make sure you do the validation in the model
accepts_nested_attributes_for :assets, allow_destroy: true
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
In your view loop through the asset to get each image
<%= #vehicle.assets.size %>
<% for asset in #vehicle.assets %>
<%=link_to image_tag (asset.image.url(:thumb)) %>
<% end %>
If am correct your problem is that asset_attributes is an array with each image having an index column and an image
Your form_for should have something similar to this and if you want you can also include preview so the upload can view their images use the bottom code for that
<div class="field">
<h3>Vehicle Image Upload</h3>
<%= f.fields_for :assets do |asset_fields| %>
<% if asset_fields.object.new_record? %>
<p>
<%= asset_fields.file_field :image %>
</p>
<% end %>
<% end %>
</div>
<div class="field">
<h4>Vehicle Image</h4>
<%= f.fields_for :assets do |asset_fields| %>
<% unless asset_fields.object.new_record? %>
<%= link_to image_tag(asset_fields.object.image.url(:thumb)),
asset_fields.object.image.url(:original)%>
<%= asset_fields.check_box :_destroy %>
<% end %>
<% end %>
</div>
Sanitize before save in controller Sanitize accepts_nested_attributes_for attributes with index.
before_action :sanitize_fields_params, :only => [:create, :update]
def sanitize_fields_params
product_free_shippings_attributes = params[:product][:product_free_shippings_attributes]
product_free_shippings_attributes.each do |index, key_value|
params[:product][:product_free_shippings_attributes]["#{index}"][:weight] = clear_decimal(key_value[:weight])
params[:product][:product_free_shippings_attributes]["#{index}"][:height] = clear_decimal(key_value[:height])
params[:product][:product_free_shippings_attributes]["#{index}"][:width] = clear_decimal(key_value[:width])
params[:product][:product_free_shippings_attributes]["#{index}"][:depth] = clear_decimal(key_value[:depth])
end
end
def clear_decimal(field)
return (field.to_s.gsub(/[^\d]/, '').to_d / 100.to_d) unless field.blank?
end

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

Resources