Rails scaffold lookup #object by something other than :id - ruby-on-rails

I want to look up an object from the database using a GET with a parameter other than :id using a generated controller.
Currently when I GET /parties/8.json
{"id":8,"Name":"testname","Date":"5 July, 2015","Location":"testlocation","Requirements":null,"Password":"","URL":"WMKQUBBC","Token":"testtoken","created_at":"2015-07-05T23:45:22.474Z","updated_at":"2015-07-05T23:45:30.293Z"}
I want to be able to GET /parties/WMKQUBBC.json and get the same response, but it currently looks up only by :id.
I tried adding it in routes.rb
get 'parties/:URL' => 'parties#show'
resources :parties
to work with my parties_controller.rb
# GET /parties/1
# GET /parties/1.json
def show
#party = Party.find_by_URL(:URL)
format.html { redirect_to #party }
format.json { render :show, location: #party }
end
but I still get a ActiveRecord::RecordNotFound exception, it seems like the action is defaulting to the standard Rails behavior for parties#show and it isn't even hitting my Party.find_by_URL(:URL) code.
Any ideas?

Related

URL create and new in Rails

I created a new Rails project. After all, I have a question that I cannot find anywhere or answer by myself so I need your help.
When you create a new object (like Person, Book), you need 2 action: NEW and CREATE.
When I create new, I have link: localhost:3000/admin/books/new
And then when I create fails it will return ERROR MESSAGE anf this link: localhost:3000/admin/books/create
If I click in url and `ENTER`. It will wrong.
I'm trying to use redirect_to or render if creation is failed. But nothing happen, sometimes it go to new page but it don't show error message.
I think is a rule in Rails. But I still want to ask that anyone have any idea to resolve this problem??? Go tonewlink witherror messageif they're failed
More details: I'm using Typus gem to create view for admin. So I can't find Routes file. I run rake routes and get:
GET /admin/books/(:/action(/:id)) (.:format)
POST /admin/books/(:/action(/:id)) (.:format)
PATCH /admin/books/(:/action(/:id)) (.:format)
DELETE /admin/books/(:/action(/:id)) (.:format)
And controller when create book:
if result
format.html { redirect_on_success }
format.json { render json: #item }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
Thanks for your helping :)
That is the normal way that Rails works. To understand what is happening, you need to understand what is HTTP verbs and how they works.
When you visit http://localhost:3000/book/new, you are making a request to the server to get (GET Verb) some information. In this case, a form to submit a new Book.
When You click submit, you are Sending (POST verb) data to the Server. On Rails, the link http://localhost:3000/book/create is available only by POST request. That is why, when you visit this link directly, it says that the route was not found.
This line:
# ...
else
format.html { render :new, status: :unprocessable_entity
end
means that, if something wrong happens, it need to render the view of new action again without redirect. This way you are able to find the errors on the object you are trying to save.
If you redirect, you will lose the actual (at the create stage) object. New object without data and error will be created on the new action:
def new
#book = Book.new
end
For this reason you are unable to access the error mensagens when you redirect. Only you can do on redirection, is setting a flash message:
if #book.save
redirect_to #book
else
flash[:error] = "An error occurred while saving Book."
redirect_to :new
end
2 resources that will hep you with that:
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
On your rake routes, you could notice that it is prefixed with admin.
GET /admin/books/(:/action(/:id)) (.:format)
POST /admin/books/(:/action(/:id)) (.:format)
PATCH /admin/books/(:/action(/:id)) (.:format)
DELETE /admin/books/(:/action(/:id)) (.:format)
Did you try it to prefixed with admin/books/new? And admin/books/create? Then notice your url: you use only book since on your routes it is books.
Try:
http://localhost:3000/admin/books/new
http://localhost:3000/admin/books/create
You shouldn't be getting that error, there (by default) is no /create path, especially with a GET verb.
Whilst you can create your own /create path, your functionality is conventional:
#config/routes.rb
scope :admin do
resources :books, :people, only: [:new, :create] #-> url.com/admin/books/new
end
#app/controllers/books_controller.rb
class BooksController < ApplicationController
respond_to :json, :html, only: :create #-> needs responders gem
def new
#book = Book.new
end
def create
#book = Book.new book_params
respond_with #book if #book.save
end
end
The above is the standardized (working) way to achieve what you want.
--
As per the routes, there is no /create path:
POST /photos photos#create create a new photo

Allow method to accept an :id param

I have created a method called verify in a controller (events_controller.rb), and I want to allow that page (verify.html.erb) to accept an object (#event), and show of that objects parameters. I'm creating a show page in essence, but I need to build some special logic into this page that I don't want to build into the show page. I have created the route, but I still get an error when I tell it to find an Event by params[:id]. The actual url it is going to is /verify.(event :id) and I believe it should be routing to events/verify/(event :id).
My error
Couldn't find Event without an ID.
routes.rb
get "verify", to: 'events#verify'
resources :events
events_controller.rb
def verify
#event = Event.find(params[:id])
respond_to do |format|
format.html # verify.html.erb
format.json { render json: #event }
end
end
Thanks Stack!
get "verify/:id", to: 'events#verify', as: "verify"
in browser go to url for example:
localhost:3000/verify/1

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.

RESTful API Ruby on Rails

I'm playing with Ruby on Rails for the first time, and have an app up and running. Here's the quick database definition:
Teams
- id: int
- name: char[20]
Answers
- id: int
- answer_value: text
I want to be able to type: "http://localhost:3000/teams/1/answers/purple" in a browser
if team 1 answers purple.
I thought that adding the following to my routes.rb file would allow me to do that, but it hasn't.
resources :teams do
resources :answers
post 'answer_value', :on => :member
end
I can see the first answer by team 1 by going to "http://localhost:3000/teams/1/answers/1" , but I don't know how to actually set values via the URI.
If I understand you correctly, in you answers controller's show action, instead of doing Answer.find(params[:id]), do Answer.find_by_answer_value(params[:id])
If you want to create an answer, please post to
/teams/1/answers
with parameter :answer => { :answer_value => 'purple' }
Don't append answer_value to the url, looks like it's record id.
You need to either over ride the to_param method in the model, which can do what you are looking for although there are some caveats - including needing to use what #Chirantan suggested with Answer.find_by_answer_value(params[:id]) for all your finders. You can find more info in this Railscast on the subject or search for to_param in the Rails API and select the entry under ActiveRecord::Integration.
Alternatively you can go with a gem like FriendlyId which provides some more comprehensive functionality and control over the URL construction.
Typing things into your browser won't perform a post, regardless of what you put in the routes, it will only perform a get.
Creating something by a get is not RESTful, and you really shouldn't do it. That said, since you say you're just 'playing around with rails', you could do this:
If you want a get to create the thing, you have to set it that way in the routes. /teams/1/answers/purple
get '/teams/:team_id/answers/:answer_value' => 'answers#create'
then in your answers controller, create action, you could just check for a params[:team_id] and params[:answer value] and make sure that gets set before the save.
def create
#answer = Answer.new()
#answer.team_id=params[:team_id]
#answer.answer_value=params[:answer_value]
respond_to do |format|
if #answer.save
format.html { redirect_to #answer, notice: 'Answer was successfully created.' }
format.json { render json: #answer, status: :created, location: #answer }
else
format.html { render action: "new" }
format.json { render json: #answer.errors, status: :unprocessable_entity }
end
end
end

Rails 3 - Nested Resources - Routing

I'm having issues with my destroy method on a nested source Product, that is tied to Orders.
After attempting to destroy an item, I'm redirecting users to my order_products_url. I receive the following routing error:
No route matches "/orders/1/products"
My destroy method looks like this:
def destroy
#product = Product.find(params[:id])
#order = Order.find(params[:order_id])
#product.destroy
respond_to do |format|
format.html { redirect_to(order_products_url) }
format.xml { head :ok }
end
end
And in routes.rb:
resources :orders do
resources :products, :controller => "products"
end
The reason why this is confusing me, is for my update method for products, I'm properly redirecting users to the order_products_url without issue. I don't understand why it works there but not here.
Thanks
order_products_url expects a parameter to be passed - either the order id, or the order object itself. Without this, it won't work properly. So using your code above:
def destroy
#product = Product.find(params[:id])
#order = Order.find(params[:order_id])
#product.destroy
respond_to do |format|
format.html { redirect_to(order_products_url(#order) }
format.xml { head :ok }
end
end
As a side note, you can shorten your routes a little:
resources :orders do
resources :products
end
Specifying the controller is redundant when it's named as Rails expects. I hope this helps!
UPDATE: I've added a link to my article about routing in Rails 3, with downloadable code samples. I updated it with a paragraph that explains named routes, in the "Things You Should Know" section:
Routing in Ruby on Rails 3
Don't you need to redirect to order_products_url(#order)?
you should be using orer_products_path (not url). If you go to the root of your app and type,
rake routes
that will give you a list of all named routes. You need to append _path to them though (returns the string representation). This is a handy little trick for figuring out named routes.
Now to your real question - of course it doesn't exist! You just deleted it! You are destroying the product instead of the product from the order!

Resources