I am trying to create a destination, but it keeps telling me in my browser that 'name' is nil when it redirects redirects to my 'show' view.
Error I receive
undefined method `name' for nil:NilClass
Here are my controller actions for new, create, and show:
def show
#destination = Destination.find_by(id: params[:id])
end
def new
#destination = Destination.new
end
def create
#destination = Destination.create(dest_params)
redirect_to user_destination_path(current_user, #destination.id )
end
private
def dest_params
params.require(:destination).permit(:name,:user_id)
end
My new form where I enter the name of the destination:
<h2>Add a destination</h2>
<div>
<%= form_for #destination do |f|%>
<%= f.label :name %>
<%= f.text_field :name %><br>
<%= f.submit %>
<% end %>
</div>
here is my read/show view:
<h3>Added destination</h3>
<div>
<p><%= #destination.name %></p>
</div>
Before all this I was getting missing required keys [:id] errors, but I seemed to fix that but for some reason I suspect that might have something to do with the issue I am having now. Let me know if you are able to spot the issue
Updated Error
No route matches {:action=>"show", :controller=>"destinations", :id=>nil, :user_id=>"1"}, missing required keys: [:id]
The main problem here is a total lack of error handling. You're not checking at all if the user provided valid input or if the record was even saved in your create method.
def create
#destination = Destination.create(dest_params)
redirect_to user_destination_path(current_user, #destination.id )
end
If the record is not saved for example due to a failed validation #destination.id is nil.
In your show method you're using find_by instead of find which just lets the error slide instead of raising a ActiveRecord::RecordNotFound error.
Your controller should actually look like:
class DestinationsController
def show
# will raise if the record is not found and return a 404 not found response
# instead of just exploding
#destination = Destination.find(params[:id])
end
def new
#destination = Destination.new
end
def create
# never just assume that the record is created unless you want
# to get kicked in the pants.
#destination = Destination.new(dest_params)
if #destination.save
# this route really does not need to be nested.
# see https://guides.rubyonrails.org/routing.html#shallow-nesting
redirect_to user_destination_path(current_user, #destination)
else
# re-render the form with errors
render :new
end
end
private
def dest_params
params.require(:destination).permit(:name,:user_id)
end
end
Related
everyone! I am new to rails and working on Codecademy tutorials. But I wanted to see if I can run the same app on my mac using VS Code and got into some roadblocks. The application is basically to create a form that takes in messages and displays it (in the index view). I wanted to explore changing the names of controller and model to what I want and guess I messed up the internal routing. Following is the controller (messagec)
class MessagecController < ApplicationController
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
render 'new'
end
end
private
def message_params
params.require(:message).permit(:content)
end
end
THe following is the model (messagesmo1)
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
The following is the routes.rb file
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'message_mo1s#create'
The following is the code in create.html.erb file
<%= form_for(#messages2) do |f| %>
<div class = "field">
<%= f.label :message %><br>
<%= f.text_area :content %>
</div>
<div class = "actions">
<%= f.submit "Create" %>
</div>
<% end %>
I am able to see the message list and able to go to create new message page. But when I submit the form, I am getting the following Routing error:
uninitialized constant MessageMo1sController Did you mean? MessagecController MessagesMController
My first questions is:
1) What am I missing in the routes.rb file?
2) Is there any rule between naming the model similar to that of the controller?
I just replicated all of the above, I think there are many things to keep in mind.
Your model file must be of name messagesmo1.rb and in this model:
class MessagesMo1 < ApplicationRecord
end
Your controller file should be of name messagec_controller.rb and in it:
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
redirect_to '/messages/new'
end
end
private
def message_params
params.require(:messages_mo1).permit(:content)
end
In the above point, look at the message_params part, it must be :messages_mo1 and not :message
No changes required in _form.html.erb file
Your migration file must be of name timestamp__create_messages_mo1s.rb and it must have:
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
In your routes.rb file, change the last route:
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'messagec#create'
Make sure all your links are updated in index.html.erb, in show.html.erb and in new.html.erb -> Like links to show, delete, edit etc. Or if your just testing remove these links.
After making above changes, run rails db:drop db:create db:migrate as it will clean your DB of old migration.
That's it, now everything should work. The main problem is naming convention should be standard across all files. So it's better to use standard convention.
It finally worked. Following are the 2 changes:
1) Instead of <%= form_for(#messages2) do |f| %>, I used a URL parameter
<%= form_for(#messages2, url:'/messages/') do |f| %>
2)As #cdadityang mentioned, I updated the params to params.require(:messages_mo1).permit(:content)
without the URL being given explicitly, I think the rails is assuming '/message_mo1' are the path. So the URL is basically taking it to 'messagec#create'
I have tried to get into rails and ruby by starting to work on a little project and have a problem I can't get around.
As I was trying to create a simple CRUD for an Object, the creation part made no sense anymore.
def create
if (params.nil? || params[:board].nil?)
return render status: 400
end
#board = Board.create(params["board"]["title"], params["board"]["description"])
#...
end
For whatever reason, it gives me an ArgumentError "wrong number of arguments (given 2, expected 0..1)". So I thought I'll simply create it myself and use the save-Method to save it into the database, but that didn't work out either:
#board = Board.new(params["board"]["title"], params["board"]["description"])
#board.save!
This gives me the NoMethodError "undefined method `reverse_merge!' for nil:NilClass".
I tried allot of debugging now but can't figure it out. And not, it's not nil, even though it's saying it's using the NilClass.
EDIT: Form Code (View)
<%= form_tag :action => 'create' do %>
<div class="fluid-container">
<p><label for="board_title">Title</label></p>
<%= text_field 'board', 'title' %>
</div>
<div class="fluid-container">
<p><label for="board_description">Description</label></p>
<%= text_area 'board', 'description' %>
</div>
<%= submit_tag %>
<% end %>
I really don't know what's going on, hopefully someone can help. Thanks in advance - PreFiX/Dominik
Instead of
#board = Board.create(params["board"]["title"], params["board"]["description"])
try
#board = Board.create(title: params["board"]["title"], description: params["board"]["description"])
You should be able to do this too
#board = Board.create(params[:board])
but for security reasons that wont work
http://api.rubyonrails.org/classes/ActionController/Parameters.html
When you try to create a new object, you should pass a hash and not strings like you did.
Replace your controller method to
def create
#board = Board.new(board_params)
if #board.save
redirect_to #board, notice: 'Board was successfully created.'
else
# render the new page
end
end
And add a private method
private
def board_params
params.require(:board).permit(:title, :description)
end
So as it stands I have a form partial which starts off as:
<%= form_for(#merchandise) do |f| %>
It works perfectly for editing the data that I have already assigned in the rails console. When I try to use it for a "new" form that creates new merchandise (in this case the singular form of merchandise, I don't have nested resources, multiple models etc.), I get a no Method error that states
"undefined method 'merchandises_path' for #<#<Class:0x64eaef0>:0x95d2370>".
When I explicitly state the URL in the form_for method:
<%= form_for(#merchandise url:new_merchandise_path) do |f| %>
I get it to open and I finally have access to the form, however in this case I get a routing error that states
"No route matches [POST] "merchandise/new"".
I decided to write out that route in my routes file which previously just had:
root "merchandise#index" resources :merchandise
After I add the route it literally does nothing. I click submit and it takes me to the form but there is no new data in the database. I am at a complete loss and have been at this for hours googling and stack overflowing and I just don't know what to do anymore. All help is seriously appreciated. I'm adding a pastebin with all my code in the following url:
http://pastebin.com/HDJdTMDt
I have two options for you to fix it:
Option 1:
Please try to do this for best practice in Rails:
routes.rb
change your routes use plural
resources :merchandises
merchandises_controller.rb
Rename your file controller and class into MerchandisesController
class MerchandisesController < ApplicationController
def index
#merchandise = Merchandise.all
end
def new
#merchandise = Merchandise.new
end
def create
#merchandise = Merchandise.new(merchandise_params)
if #merchandise.save
redirect_to merchandises_path
else
render :new
end
end
def show
#merchandise = Merchandise.find(params[:id])
end
def edit
#merchandise = Merchandise.find(params[:id])
end
def update
#merchandise = Merchandise.find(params[:id])
if #merchandise.update(merchandise_params)
redirect_to #merchandise, notice: "The movie was updated"
else
render :edit
end
end
def merchandise_params
params.require(:merchandise).permit(:shipper, :cosignee, :country_arrival_date, :warehouse_arrival_date, :carrier, :tracking_number, :pieces, :palets, :width, :height, :length, :weight, :description, :cargo_location, :tally_number, :customs_ref_number, :released_date, :updated_by, :country_shipped_to, :country_shipped_from)
end
end
Option 2:
If you want to not change many code
/merchandise/_form.html.erb
in partial file
/merchandise/new.html.erb
<%= render 'form', url: merchandise_path, method: 'post' %>
/merchandise/edit.html.erb
<%= render 'form', url: category_path(#merchendise.id), method: 'put' %>
I'm trying to make simple app. I input my first name and last name to simple <%= form_for #data do |f| %> rails form and after submitting it, app should render simple text like this. My first name is <%= data.first_name %> and my last name is <%= data.last_name %>. I don't know why but my app is saying this error:
undefined local variable or method `data' for
It's probably saying it because no params are passed to view.
Here is my code.
routes.rb
resources :data, only: [:new, :create, :index]
data_controller.rb
class DataController < ApplicationController
def new
#data = Data.new
end
def index
end
def create
#data = Data.new(data_params)
if #data.valid?
redirect_to #data
else
render :new
end
end
private
def data_params
params.require(:data).permit(:first_name, :second_name)
end
end
/views/data/new.html.erb
<%= form_for #data do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<%= f.label :second_name %>
<%= f.text_field :second_name %>
<%= f.submit 'Continue', class: 'button' %>
<% end %>
/views/data/index.html.erb
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= data.first_name %>.</p>
<p>And my second name is: <%= data.second_name %>.</p>
/models/data.rb
class Data
include ActiveModel::Model
attr_accessor :first_name, :second_name
validates :first_name, :second_name, presence: true
end
Please help to find out why params are not passing to next page. Thanks anyways :D
Your view should look like this:
<h2>Coolest app ever :D</h2>
<p>My first name is: <%= #data.first_name %>.</p>
<p>And my second name is: <%= #data.second_name %>.</p>
Also, I would suggest that calling a model something generic like Data is not a very Rails-y approach. Generally, domain models correspond to real-world things like User and Article, which are easy to understand and relate to. It'll get confusing quite fast if you use need to make another model and want to call it Data2 or something :)
Edit:
Since you specified that you do not wish to use the database, I would recommend passing in the object params through the redirect:
redirect_to(data_path(data: #data))
and in your controller's index method:
def index
#data = Data.new(params[:data])
end
Now your view should render properly, since you're passing the in-memory #data object attributes as params within the redirect. You then recreate this object in the index page or wherever you wish to redirect to.
To expand on Matt's answer, the reason you're getting NilClass errors is because:
You're redirecting to a data#show action when no show action has been enabled within your routes file. Since you've set your views up for the index, I'm assuming you want to redirect there when the #data object has been verified as valid:
redirect_to data_path
However I would recommend you follow Rails conventions and specify the data#show route within your routes.rb:
resources :data, only: [:index, :new, :create, :show]
and in your data_controller.rb:
def show
#data = Data.find(params[:id])
end
Another problem is that you're not actually saving the #data object upon creating it. The new method populates the attributes, and valid? runs all the validations within the specified context of your defined model and returns true if no errors are found, false otherwise. You want to do something like:
def create
#data = Data.new(data_params)
if #data.save
redirect_to data_path
else
render :new
end
end
Using save attempts to save the record to the database, and runs a validation check anyways - if validation fails the save command will return false, the record will not be saved, and the new template will be re-rendered. If it is saved properly, the controller will redirect to the index page, where you can call upon the particular data object you want and display it within your view.
I have a from created in Ruby on rails. The code the form looks like this:
<%= simple_form_for(#action) do |f|%>
<%= render 'shared/error_messages' %>
<%=f.label :action_name, "Action name"%>
<%=f.text_field :action_name%></br>
<%=f.input :startDate,:as => :datetime_picker, :label =>"Start date"%>
<%=f.input :endDate,:as => :datetime_picker, :label =>"End date"%>
<%=f.label :contentURL, "Content url"%>
<%=f.text_field :contentURL%></br>
<%= f.button :submit, class: "btn btn-large btn-primary" %>
<%end%>
But when I click the submit button I get this error:
undefined method `permit' for "create":String
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
All other forms a working ok, I guess it is something really obvious, just can't see it :(
I really appreciate any help, solving this problem.
Thanks!!
EDIT:
Controller code:
def create
action = Action.new(action_params)
if #action.save
flash[:success] = "New Action saved"
redirect_to "/"
else
render 'new'
end
end
private
def action_params
params.require(:action).permit(:action_name, :startDate,:endDate,:contentURL)
end
In Rails 4, you must use Strong Parameters in your controllers. Here's some explanation from the official blog. And some example:
class PeopleController < ActionController::Base
# This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
# without an explicit permit step.
def create
Person.create(params[:person])
end
# This will pass with flying colors as long as there's a person key in the parameters, otherwise
# it'll raise a ActionController::MissingParameter exception, which will get caught by
# ActionController::Base and turned into that 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap do |person|
person.update_attributes!(person_params)
end
end
private
# Using a private method to encapsulate the permissible parameters is just a good pattern
# since you'll be able to reuse the same permit list between create and update. Also, you
# can specialize this method with per-user checking of permissible attributes.
def person_params
params.required(:person).permit(:name, :age)
end
end
Notice how, in the last lines, under the private keyword, the person_params method is defined, which declares the permitted fields to be assigned by the create and update methods on top. And it's the person_params that is used for updating - the valid example - instead of the raw params array.