Updating records

So I would like to be able to update a record without having to load the edit page. Lets use checking out a book as an example. From the front page of my application I would like to be able to have a form field where I can enter a record id and select one of a couple of check boxes and then hit submit which will update the associated record with the action selected by the check box (check out, check in, etc). I am assuming I need to use a form_tag but I am not sure how to go about writing the controller and I am assuming I will need to do some custom routing.

  def update

    @barcode = params[:barcode]     @checkout = params[:checkout]

    @book = Book.find(:first, :conditions => {:barcode => [@barcode]})

    @book.update_attributes(params[:book])

  end

Just use the same technique as on the edit page but miss out the fields that you do not wish to change. Then submit to the existing update action, you do not need to change it as update_attributes only changes the fields that are provided, and leaves the others unchanged (I think that is correct, someone will correct me if I am wrong). Depending on how similar the required view is to the normal edit view you may even be able to use the same one, just hiding the fields you do not want.

By the way form_for is the conventional way now, rather than form_tag. Your edit view will likely be using that unless you picked up the code from an old tutorial or book.

Colin

Hmm, I understand what you are saying, and that is indeed what I tried to do originally but because I am not loading the edit page first I am pretty sure I cannot use the form_for method. The form_tag that I am using appears to parse the fields correctly however the data is not being processed correctly on the backend...

Parameters: {"commit"=>"Update", "action"=>"update", "barcode"=>"123456789", "checkout"=>"1", "controller"=>"books"}

I am able to type the barcode in and hit update but I am being taken to the update page rather than just processing the update method. I know it is reading the hash correctly because if I change the barcode I am getting a nil value error. Like you said, my form is simple (this is in my index.erb.html file),

<% form_tag(:action => "update") do -%>   <fieldset>   <legend>Process Return</legend>   <label>Barcode</label><%= text_field_tag 'barcode' %><br>   <label>Check Out?</label><%= check_box_tag 'checkout' %>   </fieldset> <p>   <%= submit_tag "Update" %> </p> <% end -%>

You have already seen my controller; there isn't much here so I must be doing something pretty stupid... Thanks,

Colin Law wrote:

Hmm, I understand what you are saying, and that is indeed what I tried to do originally but because I am not loading the edit page first I am pretty sure I cannot use the form_for method. The form_tag that I am using appears to parse the fields correctly however the data is not being processed correctly on the backend...

Parameters: {"commit"=>"Update", "action"=>"update", "barcode"=>"123456789", "checkout"=>"1", "controller"=>"books"}

I am able to type the barcode in and hit update but I am being taken to the update page rather than just processing the update method. I know it is reading the hash correctly because if I change the barcode I am getting a nil value error. Like you said, my form is simple (this is in my index.erb.html file),

<% form_tag(:action => "update") do -%> <fieldset> <legend>Process Return</legend> <label>Barcode</label><%= text_field_tag 'barcode' %><br> <label>Check Out?</label><%= check_box_tag 'checkout' %> </fieldset> <p> <%= submit_tag "Update" %> </p> <% end -%>

As I said it is easier to use the form_for method. Just copy the existing edit view code (which presumably includes the barcode and checkout values) and remove from the form the fields that you do not want.

The problem with the submit is that you are not putting the data in the params correctly. Try doing an edit and look in development.log to see what the params are, then do the same with yours and compare them. Your fields should be inside a hash for the complete object. But as I said if you use form_for it will all be done for you. I don't understand what problem you are having with form_for, what has it got to do with loading the edit page? You are using your new page instead of the edit page in this case.

Colin

I switched over to form_for as you suggested and that seems to be working much better but I am still running into a problem. The form appears to be submitting the correct data but I am having a problem with selecting the record correctly. Specifically, in my controller,

  def update

    @book = Book.find(:first, :conditions => ["barcode = ?",params[:barcode]])

    @book.update_attributes(params[:book])     redirect_to "/books"

  end

If I replace the params[:barcode] with a real barcode (e.g. Book.find(:first, :conditions => {'barcode' => ['123456789']})) number the form processes successfully and updates my database. When I try from the form however I am getting a null value error even though I can see the parameters being passed along in the logs so I am not sure why they are not available to the model...

Processing BooksController#update (for 10.0.1.65 at 2010-03-30 14:01:07) [POST]   Parameters: {"commit"=>"Update", "action"=>"update", "authenticity_token"=>"vq6j9DJ57+AP4kbdbFHjeqeXHiYJDr9RT+zwHVCJ7mI=", "book"=>{"bounceback"=>"1", "barcode"=>"123456789"}, "controller"=>"books"}

[4;36;1mBook Load (0.6ms)[0m [0;1mSELECT * FROM `books` WHERE (barcode = NULL) LIMIT 1[0m

I have tried every combination of :conditions I can think of but I don't seem to be able to get it to work correctly. Thanks!

Stephen

Colin Law wrote:

I switched over to form_for as you suggested and that seems to be working much better but I am still running into a problem. The form appears to be submitting the correct data but I am having a problem with selecting the record correctly. Specifically, in my controller,

def update

@book = Book.find(:first, :conditions => ["barcode = ?",params[:barcode]])

@book.update_attributes(params[:book]) redirect_to "/books"

end

If I replace the params[:barcode] with a real barcode (e.g. Book.find(:first, :conditions => {'barcode' => ['123456789']})) number the form processes successfully and updates my database. When I try from the form however I am getting a null value error even though I can see the parameters being passed along in the logs so I am not sure why they are not available to the model...

Processing BooksController#update (for 10.0.1.65 at 2010-03-30 14:01:07) [POST] Parameters: {"commit"=>"Update", "action"=>"update", "authenticity_token"=>"vq6j9DJ57+AP4kbdbFHjeqeXHiYJDr9RT+zwHVCJ7mI=", "book"=>{"bounceback"=>"1", "barcode"=>"123456789"}, "controller"=>"books"}

Look carefully at the params, you will see that you want params[:book][:barcode] in your find.

You could have found this by using ruby-debug to break into your controller where it is failing and inspecting the variables. See the guide on debugging.

I would have expected to see an id there also, are you not passing the record up to the view where the form_for is? Look at the code in the controller#edit and view#edit and copy those except for missing out the fields that you don't want in the view. Then you should get the id also and can lookup on that rather than the barcode, which would be more conventional. Unless I am misunderstanding exactly what you are doing (which would not be unusual).

By the way, could you not 'top post' it would make it easier to follow the thread. Thanks.

Colin

Colin Law wrote:

� �@book.update_attributes(params[:book])

Processing BooksController#update (for 10.0.1.65 at 2010-03-30 14:01:07) [POST] �Parameters: {"commit"=>"Update", "action"=>"update", "authenticity_token"=>"vq6j9DJ57+AP4kbdbFHjeqeXHiYJDr9RT+zwHVCJ7mI=", "book"=>{"bounceback"=>"1", "barcode"=>"123456789"}, "controller"=>"books"}

Look carefully at the params, you will see that you want params[:book][:barcode] in your find.

You could have found this by using ruby-debug to break into your controller where it is failing and inspecting the variables. See the guide on debugging.

I would have expected to see an id there also, are you not passing the record up to the view where the form_for is? Look at the code in the controller#edit and view#edit and copy those except for missing out the fields that you don't want in the view. Then you should get the id also and can lookup on that rather than the barcode, which would be more conventional. Unless I am misunderstanding exactly what you are doing (which would not be unusual).

By the way, could you not 'top post' it would make it easier to follow the thread. Thanks.

Colin

That worked great; I will post the final controller in case anyone else wants to do it but that is pretty cool. I am not using the key field because the form is on the index page - not the edit page - which means the record hasn't actually been selected at that point. The barcode field is unique but not the key field because I need to be able to store alphanumeric information as part of the barcode and the id field is an integer. While not necessarily in the spirit of ruby, doing this allows me to arbitrarily update any record in the database from anywhere in my application regardless of any nesting or other model complexities. Thanks for the help, just a couple more problems and my first real app will be done!