Rails3 Update Action Problem Nested Resource - ruby-on-rails

I've got an problem with my update action for a nested resource.
In my app, my orders have many invoices.
Creating a new invoice, I correctly end up with the following url:
/orders/11/invoices/new
And when I edit the invoice, again, it's all correct:
/orders/11/invoices/3/edit
This works fine when the save is a success, however if the validation fails, it routes back to:
/invoices/3
I have the following in my invoices controller:
def update
# #order = Order.find(params[:order_id])
# #invoice = #order.invoices.find(params[:id])
#invoice = Invoice.find(params[:id])
respond_to do |format|
if #invoice.update_attributes(params[:invoice])
format.html { redirect_to(order_invoice_path(#invoice.order, #invoice), :notice => 'Invoice was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #invoice.errors, :status => :unprocessable_entity }
end
end
end
def edit
#invoice = Invoice.find(params[:id])
3.times { #invoice.invoice_items.build }
end
I'm assuming I need to edit the #invoice.errors part but I don't know what to change it to?
Any help appreciated. Jx

When updating failed, you use "render" (comparing with the "redirect_to" in the succeeding path), this brings you to invoice editing path by default. You can use "redirect_to" here to keep the URI path you want, but need remembering to preserve the models' states so your users don't need to fill the entire form all over again.
A detail instruction can be found here: How to make a render :edit call show the /edit in the address bar
Yan

in your form you should add your order, like this:
<%= form_for [#order, #invoice] ... do |f| %>
...
<% end %>
And then uncomment this two lines
# #order = Order.find(params[:order_id])
# #invoice = #order.invoices.find(params[:id])
so your form will send its request to POST /orders/XX/invoices/XX

Related

invalid signature error for edit product and new product form submission rails

Hi I am currently working on a web marketplace app for an assignment that allows users to upload items for sale with images attached, and to edit those listings.
Currently i have utilised simple forms for the edit and add product pages and those work fine but when I click update or add product I get the below error:
ActiveSupport::MessageVerifier::InvalidSignature in ProductsController#create
the error pointed out that line 3 of the below code is the problem:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: "Product was successfully created." }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
My product.rb file looks as such:
belongs_to :user, :optional => true
has_one_attached :picture
end
The simple forms work but for reference the form html looks like such:
<%= simple_form_for#product do |f| %>
<h1 class="heading">Edit Product</h1>
<%= render 'form', product: #product %>
<% end %>
Any help I can get would be appreciated.
When you use the respond_to do |format| method, you need to supply the actual formats that the code should respond to. So, your controller probably needs to look something like this:
def create
#product = Product.new(product_params)
#product.user_id = current_user.id
respond_to do |format|
format.html do
if #product.save
...
end
end
end
But I'd also ask: why are you using respond_to here if you only expect to process the result of a web form? It's something you may need to do at some point, but isn't required initially and adds complication.
If that doesn't resolve the issue, we'll probably need to see your strong-params function (product_params), the Product model definition and your view, at least. For example, you may need to structure the line more like this, because your params may well not exactly match the fields in your model.
#product = Product.new(id: product_params[:id], name: product_params[:name])
Rails adds a special authentication code when it creates a form, to help stop bad actors from spamming or hacking your form. If your page is changed after the code is created, it will become out of date and be rejected, but I'd expect a more specific error if that was happening. Fingers crossed!

Redirect to previous page with params attached

I have a review page for schools that i link to like this;
<%= school_reviews_path(school_id: #school.id) %>
Generating a link like this;
http://address.com/school_reviews?school_id=1
However, under the reviews is a form that create a new review posting it create action of schools_reviews controller
However, after creation of the review i want to rerender the page with the accompanying parameters i.e school_id=1
I have tried several methods like this but it aint working instead it redirects to http://address.com/school_reviews without the params meanig we do not fetch the right information.
def create
#review = SchoolReview.new(params[:review])
respond_to do |format|
if #review.save
format.html { redirect_to school_reviews_path(:school_id => #review.school_id) }
format.json { head :no_content }
else
format.html { redirect_to school_reviews_path(:school_id => #review.school_id) }
format.json { head :no_content }
end
end
end
Any ideas, will be greatful.
The problem here is that your form, most likely, isn't setting the school id anywhere, so let's make your code be more like what it should be, first setup routes like this:
resources :schools do
resources :school_reviews
end
Your controller name doesn't change, but the implementation will change to be as follows:
class SchoolReviewsController < ApplicationController
before_filter :load_school
def create
#review = #school.school_reviews.build(params[:review])
respond_to do |format|
if #review.save
format.html { redirect_to school_school_reviews_path(#school) }
format.json { head :no_content }
else
format.html { redirect_to school_school_reviews_path(#school) }
format.json { head :no_content }
end
end
end
protected
def load_school
#school = School.find(params[:school_id])
end
end
You will also have to change the form_for call at your view to something like:
<%= form_for( [#school, #review] ) do |f| %>
Your form content here.
<% end %>
Your link will change as well to:
<%= school_school_reviews_path(#school) %>
You might also want to change the SchoolReview model just to Review to remove the school_school_ from the URLs.
And, as usual, read the documentation on routes to understand what's going on.

How do I define a custom URL for a form confirmation page?

I am creating a basic product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On successful submit of the form, I would like to redirect to a custom '/thanks' page in which I thank users for their interest in the product (and also encourage them to complete a short survey.)
Currently, successful submits are displayed at "/invites/:id/" eg "invites/3" which I do not want since it exposes the number of invites that have been submitted. I would like to instead redirect all successful submits to a "/thanks" page.
I have attempted to research "rails custom URLs" but have not been able to find anything that works. The closest I was able to find was this Stackoverflow post on how to redirect with custom routes but did not fully understand the solution being recommended. I have also tried reading the Rails Guide on Routes but am new to this and did not see anything that I understood to allow for creating a custom URL.
I have placed my thanks message which I would like displayed on successful form submit in "views/invites/show.html.haml"
My Routes file
resources :invites
root :to => 'invites#new'
I tried inserting in routes.rb:
post "/:thanks" => "invites#show", :as => :thanks
But I don't know if this would work or how I would tell the controller to redirect to :thanks
My controller (basically vanilla rails, only relevant actions included here):
def show
#invite = Invite.find(params[:id])
show_path = "/thanks"
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
# GET /invites/new
# GET /invites/new.json
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
# POST /invites
# POST /invites.json
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
#format.js { render :action => 'create_success' }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
#format.js { render :action => 'create_fail' }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
It would seem as if creating a standard URL for displaying a confirmation would be relatively straightforward. Any advice on how to achieve this would be appreciated.
I guess you want to redirect after your create action, which is executed when the form is submitted.
Just add redirect_to in the following way:
def create
#invite = Invite.new(params[:invite])
if #invite.save
...
redirect_to '/thanks'
else
...
redirect_to new_invite_path # if you want to return to the form submission page on error
end
end
I omitted some of the code for brevity.
In your routes add:
get '/thanks', to: "invites#thanks"
Add the thanks action to your invites controller:
def thanks
# something here if needed
end
And create a thanks.html.erb page in app/views/invites.
I would do get "/thanks" => "invites#thanks" in routes.rb and then add this in your controller:
def thanks
end
Then add a file app/views/invites/thanks.html.erb with your thank-you content.
You could create a route like this:
resources :invites do
collection do
get 'thanks'
end
end
This will also create a path helper called thanks_invites_path.
It will be at the invites/thanks path, but if you want it to be on/thanks, you could just do as Jason mentioned:
get "/thanks" => "invites#thanks", :as => :thanks
The as part will generate a helper to access that page: thanks_path.
You would need a extra action in the controller called thanks, and put whatever info you need inside, and also you will need a additional view called thanks.html.erb
Since you want everybody to go to that page after a successful submit, in your create action you would have:
format.html { redirect_to thanks_invites_path} (or thanks_path), what ever you choose, when you name the route you can check it with rake routes if it's okay, and whatever rake routes says, just add _path at the end.

Rails send event when editing record aswell as create?

I have the following code in my controller:
# GET /kases/1/edit
def edit
#kase = Kase.find(params[:id])
respond_to do |format|
format.html { render :layout => 'kaseshow'} # show.html.erb
format.xml { render :xml => #kase }
format.pdf { render :layout => false }
end
end
# POST /kases
# POST /kases.xml
def create
#company = Company.find(params[:kase][:company_id])
#kase = #company.kases.new(params[:kase])
if #kase.save
UserMailer.deliver_makeakase("xxxxxx#xxxxxxxx.com", "Highrise", #kase) if params[:sendtohighrise]
UserMailer.deliver_makeakaseteam("xxxxxxxx#xxxxxxx.co.uk", "Highrise", #kase) if params[:notify_team_of_creation]
#kase.create_freeagent_project(current_user) if params[:send_to_freeagent]
redirect_to(#kase)
#flash[:notice] = 'Case was successfully created.'
flash[:notice] = fading_flash_message("Case was successfully created.", 5)
else
render :new
end
end
I am trying to make it so if the user edits a case and then selects the Send to Freeagent tickbox:
#kase.create_freeagent_project(current_user) if params[:send_to_freeagent]
then the case is resent to Freeagent (online accounting software). I'm not worried about dealing with duplicates because if the case already exists in Freeagent then the user won't need to resend it.
Is this possible?
Where is your 'update' method? The edit is only called when you are loading the data and rendering the page the user will see to edit the record. When save/update/submit is clicked on that page, it should be calling the 'update' method in the controller. So, you should be able to put that same line into the 'update' method in the true condition of the if block that looks something like this:
if #kase.update_attributes(params[:kase])
#your code would go here
#kase.create_freeagent_project(current_user) if params[:send_to_freeagent]
#the rest of the code would go here
end
It is, you could create checkbox/radio/hidden field in your edit form.
If you want to create some models for the Kase, you could as well look at accepts_nested_attributes

How to stay at same url when validation fails

I'm using Ruby on Rails 2.3.8 and I've got a registration form in which I receive a parameter as follows: /registration/4, which 4 is the id of a user who recommended the user that is about to register in the website.
The problem is that if the validation fails when the user submits the registation (the form renders to the controller users, action create_particular) the site will redirect to /users/create_particular, and therefore I lose the parameter with value 4 that I had before. Besides, I want the user to stay at the same url, which is /registration/4
How can I do that?
Then you should rewrite your create method. You should use redirect_to :back instead of render :action
UPD
def new
#word = Word.new(params[:word])
#word.valid? if params[:word]
end
def create
#word = Word.new(params[:word])
if #word.save
redirect_to #word
else
redirect_to new_word_path(:word => params[:word] )
end
end
Looks quite dirty, but this is just a scratch
UPD 2
This is really not the best solution, but it works
# routes.rb
match 'words/new' => 'words#create', :via => :post, :as => :create_word
# words_controller
def new
#word = Word.new
end
def create
#word = Word.new(params[:word])
respond_to do |format|
if #word.save
format.html { redirect_to(#word, :notice => 'Word was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
# views/words/new.html.erb
<%= form_for(#word, :url => create_word_path) do |f| %>
...
<% end %>
Submit to the current URI (e.g. action=""). When the submission is valid, redirect. POST->Redirect->GET is a good habit.
From the top of my head:
Edit your controller (registrations_controller.rb file). Create method by default contains following piece of code:
if #registration.save
format.html { }
format.xml { }
else
format.html { }
format.xml { }
end
Add redirect_to (:back) between brackets to else format.html{}
Ok I solved the problem by doing the following:
1) I created two routes with the same path, but with different conditions method (one it's post and the other one is set to get)
2) I changed the form in order to post to the POST action defined above
3) I added render => :my_action when the validation fails
So that's pretty much it.
Thanks anyway for all your help.
Hidden field. That user ID param has a name by which you extract it in your controller, right? So just put that value in a hidden field of the same name, then it will survive a round-trip.
For example:
<%= hidden_field_tag :referring_user_id, params[:referring_user_id] %>

Resources