Rails: update_attributes not updating all attributes - ruby-on-rails

I have a simple model called Discussion which has a boolean column called resolved.
In my form, I have the following code
<%= form_for(#discussion) do |d| %>
...
<%= d.check_box :resolved %>
<% end %>
And in my controller, I have the following:
def update
#discussion = Discussion.find(params[:id])
if #discussion.update_attributes(params[:discussion])
etc...
end
end
When I submit the form, I can see that the parameters are being sent to the server...
Parameters: {"utf8"=>"✓", "authenticity_token"=>"AsGsRHwiVva/+kTrBs0IjLeZwj1ZmXBuKZr9Pg/N6Xk=", "discussion"=>{"shortdesc"=>"Talk about something.", "content"=>"Try to update check box.", "resolved"=>"1"}, "commit"=>"Update Discussion", "id"=>"1"}
But the query doesn't include anything about updating that field.
AREL (14.9ms) UPDATE "discussions" SET "content" = 'Try to update check box.', "updated_at" = '2011-07-18 17:53:50.783176' WHERE "discussions"."id" = 1
Any idea on what I'm missing?

There are 4 reasons why this could be happening:
resolved is already set to true in the database.
You defined the resolved= method in your model and it no longer sets the attribute.
You have attr_protected :resolved.
You have attr_accessible but do not have :resolved in the list.

does your boolean column have a default? If it defaults to true - rails might not bother adding it to the set of attributes.
Alternatively, have you got attr_protected set for that column? if so - rails will never add that field to the attributes using update_attributes. You'll need to do that manually.

All,
I had recently begun rails and was taking over someone else's code and found another scenario that was not covered here that was getting me.
When using the style pointed to by this link, the params are formed in a separate method. I had to update the "_params" method to add it to the allowable list.
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html

Are you sure that the element is sended it?
I mean, form element without checked the element are not sended, so you have to put an hidden checkbox for default false.

Related

Issue with Rails multi select options field

I've set in my model('Bambino') a multi-select field to assign a value to the string attribute 'status'. Find the code below from my form partial:
<%= f.select(:status, options_for_select([['segnalato','segnalato'],
['inserito','inserito'],['drop','drop'],['concluso','concluso']])) %>
When I want to edit my record the edit form does not give me back the previous stored value but sets automatically the default value to 'segnalato' (E.g.:if I create a new record setting the status to 'inserito' and after I want to edit the record I get the edit form with the default value of 'segnalato' while I am expecting to see in the field 'inserito').
In this way when you edit a record chances to make a data entry mistake are very high. Why so? Is there a way to retrieve the proper 'status' value that was assigned when the record was created? Thanks
Are you sure that #your_record.status is equal to one of those values? Check it out before any further debugging.
Whilst Andrey Deineko's answer is probably the one you want, there is a better way to achieve what you're doing: enum.
#app/models/bambino.rb
class Bambino < ActiveRecord::Base
enum status: ['segnalato', 'inserito', 'drop', 'concluso']
end
This will give you the ability to use the following:
<%= f.select :color, Banbino.status.to_a.map { |w| [w.humanize, w] } %>
This will store a number for the status, whilst allowing you to define what each number means. It won't do anything about loading a pre-selected object (that's what Andrey's answer will do), but will give you the ability to make your application & select more succinct.

Rails 3 virtual attributes not saving

I have a model with virtual attributes:
attr_accessible :published_at
def published_at_text
I18n.localize(published_at, format: :long_no_day_with_seconds) if published_at
end
def published_at_text=(text)
self.published_at = Chronic.parse(text)
end
This works fine in the unit tests, but does not save when the published_at_text field is changed in the view. I've tried using attr_accessible :published_at_text, and adding published_at_will_change! to the setter method, but I can't get this to work.
The development.log shows that the changed value of published_at_text is being passed in, but adding a call to Rails.logger in the setter seems to indicate that it's not even getting called.
What am I missing here?
Well, you do not provided your controller method that created your object using the params hash, but I can make a guess.
You should call explicity the setter with the parameter passed to the controller.
Then checks if the value gets updated on your model.
Found it: the fields for the virtual attribute weren't being passed back as part of the article, so they weren't being shown in the params hash.
Replacing this:
<%= text_field_tag 'article_published_at_text', #article.published_at_text, class: 'text_date show_seconds' %>
with this:
<%= text_field_tag 'article_published_at_text', #article.published_at_text, name: 'article[published_at_text]', class: 'text_date show_seconds' %>
fixed the problem.

how is collection_select processed in the controller?

I made a drop down list using collection_select
<%= collection_select(:page, :user_id, #users, :id, :full_name) %>
that part works fine. I am having trouble on saving it/processing it using the controller, the APIdock isn't very helpful on that part and I haven't been able to get the one example I found to work. Can anyone explain to me how I can process the selected value in the controller?
You will have a value
params[:page][:user_id]
which will correspond to the value selected in the form. You can see it inspecting the params variable.
IT is a number, the ID of the selected user. You could load the user by
#user = User.find(params[:page][:user_id])
but it's useless. In fact, if the user_id property of the page is accessible, then with the usual
#page.update_attributes(params[:page]) # in the update action
or
#page.create(params[:page]) # in the create action
you will get the user in the page as #page.user.
To store page values in model which should specify 'has_many :pages' in user.rb.
#user = User.find(params[:user_id])
#user.pages = params[:page]
params[:page] returns an array of values which will be store in current model record.

Populate a form with the new()-method

Problem solved. HTML5 localStorage messed with me.
I'm trying to populate a form with parameters from the new()-method, and I can't get it to work.
Every user has default values for the form saved in the database(in a table called defaults), and when you create a new record I want it to be populated with the values from that table.
#default = Default.find_by_user_id(current_user.id)
#invoice = Invoice.new(:title => #default.title, :company_information => #default.company_information)
render 'create'
and then in my view:
form_for #invoice, :url => { :action => "create"} do |f| ...
What happens is that the values that are default for invoice are created, but not the ones created in the new()-method.
The weirdest part is that when I check the source code after the page is loaded, the inputs value attributes is filled with the correct information, but not rendered on the page...
What you're doing here:
Invoice.new(:title => #default.title, :company_information => #default.company_information)
Makes sense and should work…unless those fields are protected from mass assignment.
class Invoice << ActiveRecord::Base
attr_accessible :some, :other, :fields
...
end
This would allow you to set :some, :other, (and) :fields when you initialize your Invoice object, but it will prevent you from setting any other "attributes".
Strange, I don't see anything wrong with what you are trying to do... maybe something on the browser side (javascript, css, etc) is fowling things up?
Check to see if there is something selectable inside the form inputs or try creating a vanilla form without any javascript or css. Or, you might even try simply printing the contents of the attribute in the html (without using input/textarea tags) using something like:
<%= #invoice.title %>
This will at least help confirm that the default values where indeed set. Additionally, using:
<%= f.object.title %> # place me inside the form_for block
will help you confirm that the form builder instance also has the correct value.
Good luck.

How do I get the HTML 'name' attribute a rails form builder will generate for a certain field?

When you've got a form field such as this:
<%= f.text_field :last_name %>
it will generate this in HTML:
<input id="person_last_name" name="person[last_name]" size="30" type="text" />
I'd like to know if there's any way to get the name attribute (in this case "person[last_name]") that will be generated.
It seems a bit of an odd thing to want to get but I've got my reasons! I also can't be bothered launching into a lengthy explanation too.
After inspecting the form object, I found that you can get the object_name from it.
So this worked well for me: "#{f.object_name}[field_name]"
Which will generate: object[object_attributes][0][field_name]
Well, as expected, the comment you have is quite true :)
The place in the source where this happens is the InstanceTag class to which all the tag generation drills down. The method is called tag_name.
ActionView::Helpers::InstanceTag.new(
ActiveModel::Naming.param_key(#object_in_form_for),
:your_attribute,
:this_param_is_ignored
).send(:tag_name)
Also there is tag_name_with_index attribute which accepts index as first parameter. Also both tag_name and tag_name_with_index have optional parameter multiple = false, which is used for arrays (just adds [] to the end of the generated name).
Finally, there are similar methods if you need id instead of name - tag_id and tag_id_with_index respectively.
Neutrino's answer is great. Just want to add what I found, may not be exactly right. But it worked for me.
Find below method in action_view/helpers/form_tag_helper.rb
def sanitized_object_name
#sanitized_object_name ||= #object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
end
Hope it helps.

Resources