going against REST conventions in Rails forms - ruby-on-rails

Let me be clearer.
my app has two tables/models: Bikes and Cars
I have a form on page views/cars/index
However, even though the form is in views/cars/index, it has attributes (:pedals and :handlebars) for the Bikes table.
1) Which controller should the form on cars/index with attributes for the Bikes model be submitted to ?
2) What does the form have to look like (in terms of submitting from a view in the Cars index to Bikes model? <%= form_for(#car) do |f| %> OR <%= form_for(#bike) do |f| %>
3) What changes do I have to make to the route when I submit from the index of views/cars/index to insert data into the bikes table? why did I get a missing controller error message when I tried the solution posted by #axsuul
below this line is an earlier (maybe less clear) attempt to explain the problem but i don't think it was clear
this is a form that I have in views/cars/new.html.erb
<%= form_for(#car) do |f| %>
<% if #car.errors.any? %>
and the results are visible in views/cars/show and views/cars/index
However, if I want to submit a form from views/cars/index (Yes I know you don't usually submit from index), and then have it show in a completely different model views/bikes/index, what do I have to change to this?
<%= form_for(#car) do |f| %>
<% if #car.errors.any? %>

If i understand your question right you have a form for creating bikes on your cars/index page.
In that case, create a new bike in the cars controllers index action,
class CarsController < ApplicationController
...
def index
#cars = Car.all
#bike = Bike.new
end
...
end
and use that to create the form,
<%= form_for(#bike) do |f| %>
...
which left like that will be handled by bikes controllers create action.
I still consider this RESTful...

You can choose whatever URL you want to submit to by doing
<%= form_for(#car), :url => create_car_path do |f| %>
for the route
post '/views/cars/index', "cars#index", :as => :create_car
If you are asking to customize which view to render after submitting, you do that in the controller
def index
#car = Car.new(params[:car])
if #car.save
render 'bikes/index'
end
end
Hmm, so from what you explained, you are probably looking for something along the lines of
bikes_controller.rb
def create
#bike = Bike.new(params[:bike])
if #bike.save
redirect_to cars_path and return # this should go back to /cars if successful
end
render 'cars/index' # or render /views/cars/index.html.erb
end
cars_controller.rb
def index
#bike = Bike.new
end
index.html.erb
<%= form_for #bike, :url => create_bike_path do |f| %>
routes.rb
get 'cars' => "cars#index", :as => :cars
post 'cars' => "bikes#create", :as => :create_bike

Related

Associating two models together in Rails 4+

I have a User model which is working under Devise with no problems (using devise sanitizer to update fields, so no UsersController)
I am working on creating a Quiz model, which belongs_to the User model, and the User model has_one Quiz. In my routes, I have: resources :users, :quizzes (is this supposed to be quizzes or quizs? I know that Rails pluralizes but couldn't seem to find which it'd be in this case).
In my views, I'm trying to open up a modal (which works) and inside have it populated with fields that a User can enter in questions they want (q1 through q5 being the database fields).
Inside the modal content area, I have the code:
<%= form_for #quiz, url: {action: "new"} do |f| %>
<%= f.submit "Create" %>
<% end %>
and I get the error "First argument in form cannot contain nil or be empty"
Inside my quizzes controller, I have defined new as
def new
#quiz = Quiz.new
end
I would greatly appreciate some assistance here! Thank you.
In your WelcomeController action: index add this line to initialized #quiz
def index
#quiz = Quiz.new
end
hope you made a good progress in your project.
shoudn't it be like following
<%= form_for #quiz do |f| %>
<%= f.submit "Create" %>
<% end %>

Rails - inserting multiple records/dynamic form

I'm working on a dynamic form in a Rails app, and I need to insert a variable number of records into a model in a single form submission. I've done this using PHP -> MySQL/Postgres before, but I have no idea how to do it in Rails.
Ultimately, users should be able to create any number of records to be inserted, but in my example below, I'm limiting it to 2... let me see if I can do that, first...
Here's the form - the ids all get a unique suffix because they are being populated dynamically from localStorage objects on submission.
new.html.erb
<%= form_for #entry, html: {id: :new_entry_form} do |f| %>
<% for i in 0..1 %>
<%= f.text_field :name, :id => 'name_#{i}' %>
<%= f.text_field :day, :id => 'day_#{i}' %>
<% end %>
<% end %>
Here's the associated controller - I'm sure that this is missing something, but I don't know what.
def new
#entry = Entry.new
end
def create
#entry = Entry.create(entry_params)
redirect_to "http://localhost:3000/entries"
end
private
def entry_params
params.require(:entry).permit(:name, :day)
end
Any help would be much appreciated.
Follow this link it shows how to create multiple object in one form submit:
http://vicfriedman.github.io/blog/2015/07/18/create-multiple-objects-from-single-form-in-rails/

Trying to fire an action in my controller when a selection in a drop down menu is made but getting a no route matches error

I'm building a web interface to accompany a mobile app I'm building. I have a drop down select menu that lists a bunch locations.
On selection of a location I want to make a call to a method in my controller and grab some destinations within the location that was selected (each location has several destinations).
I then would like to render my show template with these results allowing the user to select a destination and make a booking.
This is what I have so far:
My view with a list of resorts:
<%= form_tag :url => { :action => :show } do %>
<%= select_tag :resort , options_for_select(#resorts), :prompt => 'Select Resort', :onchange => 'submit()' %>
<% end %>
Controller:
class HomeController < ApplicationController
def index
#resorts = ["A","B", "C", "D", "E"]
end
def new
end
def edit
end
def create
end
def show
#activities = Parse::Query.new("Activity").tap do |a|
a.eq("resort", params[:resort])
end.get
end
end
Just slightly confused. Using form_for makes more sense to me with CRUD in mind and also because the form is object based.
I'd like to just take the selected resorted and pass it into a method in my controller that goes into a database and grabs a bunch of destinations. I then want to list these destinations on my show page where a user can click and be taken to another page where they can make a booking at that destination.
My above code doesn't work. I have resources :home in my routes file.
However when I try to load my page with the form I get:
No route matches {:action=>"show", :controller=>"home"} missing required keys: [:id]
How do I pull this off?
I went on my lynda account and pulled up a rails essential tutorial which I'll have to use to refresh my memory some time tomorrow but the tutor doesn't cover use of select_tag.
Would appreciate some help here
Thanks for your time
So a few thoughts. Not sure why you are using form_tag and also not sure why you aren't using Rails idiomatic conventions.
Declare a resource in your routes for #resorts, like so:
resources :resorts
Then just use Rails form_for helper like:
<%= form_for #resorts, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
<%= f.select :resort, (insert your other options) %>
<%= f.submit "Create" %>
<% end %>
I have not tested the above code, so play around with it, but that should work.
However, let me save you some headache. Checkout SimpleForm.
For your models, you would want to setup an association between your locations and destinations.
class Location < ActiveRecord::Base
belongs_to :resort # or whatever the relation is
has_many :destinations
end
class Destination < ActiveRecord::Base
belongs_to :location # This assumes there is just a one-to-many relationship between Location and Destination
end
Make sure you have a LocationsController with all the actions.
In this case, your SimpleForm form would look something like this:
<%= simple_form_for #locations do |f| %>
<%= f.input :name %>
<%= f.association :password %>
<%= f.button :submit %>
<% end %>
That approach will make your life much easier. Take a look at the collections methods in Simple Form. Rails can make your life difficult with the built in form helpers.
Hope that helps!
In your routes, add
get '/choose_resort' => 'home#show' #you can name the get whatever
Then in your form...
<%= form_tag choose_resort_path do %>
That being said... you should have your query at a separate endpoint, and redirect to the show page. That should get you moving, methinks.
The show action needs an id of the object you are showing. Change your controller:
class HomeController < ApplicationController
def index
#resorts = [["A",1], ["B",2], ["C",3], ["D",4], ["E",5] ]
end
And your view
<%= select_tag :id , options_for_select(#resorts), :prompt => 'Select Resort', :onchange => 'submit()' %>
That gives your show action the proper resort id. You'll have to adjust that action to find the right activities relevant to the resort.

Rails Form Action

I have a form like this in my html page
<%= form_for '/purchases', html: {class: "form form-horizontal validate-form", novalidate: "novalidate"} do |f| %>
But the form action is like this when i inspect the form
action = '/purchases/new'
I want the action to be just /purchases
and my controller method is like
#purchase = OrderItems.new
If you have your routes configured properly, you just need:
<%= form_for #purchase, html: {class: "form form-horizontal validate-form", novalidate: "novalidate"} do |f| %>
of course, provided you set #purchase in your new action:
#purchase = Purchase.new
I think you're getting confused between form_for and form_tag
--
form_for is for objects -
#app/controllers/purchases_controller.rb
Class PurchasesController < ApplicationController
def new
#purchase = Purchase.new
end
def create
#purchase = Purchase.new(purchase_params)
#purchase.save
end
private
def purchase_params
params.require(:purchase).permit(:purchase, :attributes)
end
end
The important thing to note with form_for is how it will build your form out of the ActiveRecord object you define. This is vitally important, and is at the root of your error:
<%= form_for #purchase do |f| %>
This will build all the different attributes of the form (including the action attribute), from the ActiveRecord object itself. This means if you populate your form_for with anything other than an object, you're going to get into trouble (as exhibited by your error)
--
form_tag is for data -
<%= form_tag your_path do %>
...
<% end %>
This might be better suited to your circumstances, as it allows you to create a "standalone" form - one which gives you the ability to send non-model-centric data to your application
We use form_tag implementations for the likes of search facilities etc
Solution
As Marek pointed out, you need to populate your form_for with an ActiveRecord object. To do this, you need first ensure you have initialized the object in your controller's new action, before passing the value to the form:
#app/controllers/purchases_controller.rb
Class PurchasesController < ActiveRecord::Base
def new
#purchase = Purchase.new
end
...
end
#app/views/purchases/new.html.erb
<%= form_for #purchase do |f| %>
I would just like to elaborate on the answers presented here. The way form_for works is it bases the form input names on the first argument passed to it. You can pass string or symbol as the first argument, and it should work, just like your example.
= form_for :purchase do |f|
= f.text_field :order
will result in a form that will submit to the current url and will contain 1 text field with the name purchase[order].
Marek's answer is correct however it doesn't answer your question. In cases where the object of the form, by convention, doesn't match the controller that will handle the request, you can pass a url option to form_for.
# controller
def new
#purchase = OrderItem.new
end
# view
= form_for #purchase, url: '/purchases' do |f|
= f.text_field :order
This will create a form with action set to /purchases but the name of the text field will be order_item[order].
FINAL NOTE
form_for is not exclusively used for active record objects as stated in the API docs

Routing a form/create object post request in rails--problems with the form and routing request

I'm pretty new to rails so sorry if this is poorly worded... for a course, I'm creating an app that lets you add (post) new instances of classes called Planets and Moons. Moon is supposed to be a child of Planet. Right now I can create both in separate database tables, using three route requests:
get "planets" => "planets#index"
get "planets/new" => "planets#new"
post "/planets" => "planets#create"
(This create a form page to register new planets. Substitute "moons" and the moon class works the same, including create, from it's own form page.)
I have a fourth route for a planets show-by-id page
get "planets/:id" => "planets#show"
On this planets id show page, I want to create an "Add a moon" form that takes the param ID for the planet, lets the user enter a moon name, and sends the post request to create the new instance.
I don't think I know enough about forms and routing.
My form for the new moons is
<%= form_for #moon, url: "/planets/:id" do |f| %>
<p><strong>New Moon Name:</strong></p>
<p>Name: <%= f.text_field :name %></p>
<% Planet.find(params[:id]: :planet_id %>
<p><%= f.submit "Create!" %></p>
<% end %>
The three "moon" routes I have:
1) get "moons" => "moons#index"
2) get "moons/new" => "moons#new" (goes to a form I was using to test, I don't know if I need this)
3) post "/planets/:id" => "moons#create" (where I think the problem is)
I think I may need to use interpolation to get the planet ID from the params into the moon posting form so help there is great, but I'm more concerned with getting past the line:
<%= form_for #moon, url: "/planets/:id" do |f| %>
I get error: "First argument in form cannot contain nil or be empty"
This is what I have defined in the moons controller...
def new
#moon = Moon.new
end
def create
moon_attributes = params[:moon]
Moon.create({
name: moon_attributes[:name],
planet_id: moon_attributes[:planet_id]
})
end
I hope this question makes sense. Thanks in advance!
To give you some perspective, here's what you need to do:
#config/routes.rb
resources :planets, only: [:index, :new, :create, :show] do
resources :moons, only: [:create] #-> planets/:id/moons
end
This uses the resourceful routing structure in Rails, giving you the paths you have already anyway.
In terms of your form, you'd be best doing this:
#app/views/planets/show.html.erb
<%= form_for #moon do |f| #-> should route to moons_controller#create %>
<p><strong>New Moon Name:</strong></p>
<p>Name: <%= f.text_field :name %></p>
<p><%= f.submit "Create!" %></p>
<% end %>
#app/controllers/moons_controller.rb
class MoonsController < ActiveRecord::Base
def create
moon = Moon.new(moon_params)
moon.save
redirect_to planets_show_path(planet)
end
private
def moon_params
params.require(:moon).permit(:name).merge(planet_id: params[:planet_id])
end
end

Resources