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!