I am working on a toy web app to practice Ruby on Rails, and I've run into an issue that I haven't been able to figure out in the documentation. Essentially, I have a form to create tasks and relate them to users, but the form is issuing a 302 error when submitted and the record is not being saved.
I have two models, User and Task
User
class User < ApplicationRecord
has_many :tasks
end
Task
class Task < ApplicationRecord
belongs_to :article
end
The idea is simple, a user can have many tasks. The migrations for these are working fine.
I have created a (partial) form for creating tasks:
_compact_form.html.erb
<%= form_for( [#currentUser, #currentUser.tasks.build] ) do |f| %>
<p>
<%= f.label :label %>
<%= f.text_field :label %>
<%= f.label :complete %>
<%= f.check_box :complete%>
<%= f.submit %>
</p>
<% end %>
and this is attached to a dashboard view, which is trivially wired up to a dashboard controller:
class DashboardController < ApplicationController
def index
#currentUser = User.find(1)
end
end
All pretty straightforward so far. My tasks controller has a few methods to manage creation so far.
Tasks controller:
class TasksController < ApplicationController
def new
end
def show
#user = User.find( params[:user_id] )
end
def create
#user = User.find( params[:user_id] )
#task = #user.tasks.create( task_params )
redirect_to '/'
end
private
def task_params
params.require(:task).permit(:label, :complete)
end
end
And now we're at the point where I get confused. I've manually inserted a task into my database and I'm able to retrieve it just fine, but for some reason, my create route is giving an HTTP 302 error. I have set up my routes like so:
resources :users do
resource :tasks
end
root 'dashboard#index'
get 'dashboard/index'
and rake routes outputs
Prefix Verb URI Pattern Controller#Action
new_user_tasks GET /users/:user_id/tasks/new(.:format) tasks#new
edit_user_tasks GET /users/:user_id/tasks/edit(.:format) tasks#edit
user_tasks GET /users/:user_id/tasks(.:format) tasks#show
PATCH /users/:user_id/tasks(.:format) tasks#update
PUT /users/:user_id/tasks(.:format) tasks#update
DELETE /users/:user_id/tasks(.:format) tasks#destroy
POST /users/:user_id/tasks(.:format) tasks#create
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
root GET / dashboard#index
dashboard_index GET /dashboard/index(.:format) dashboard#index
so the create route appears to be there, and it appears to be correct.
Now for the strangest part! If I use my form, this is what I see in the console:
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.1ms) begin transaction
(0.1ms) commit transaction
This appears to be the correct code in my tasks#create method, so I'm pretty confused where the data is going. Can anyone shed some light into next steps for debugging this? I have spent some time creating different forms and working through the examples in this guide, but I seem to have made a mistake somewhere along the lines and I can't find it.
You should declare your redirection inside an respond_to block, redirects without format declaration cause 302 status responses because it doesn't know which kind of response to give for redirection, so it assumes your client would process the location header field that goes with the response, you should do something like this:
respond_to do |format|
format.html { redirect_to '/' }
end
302 status isn't an error, it is an indication of redirection and the redirected location is being sent as HTTP header. As far as model not being saved, you should provide better logs results, it could be a vast number of issues, like unpermitted params, validations, database error...
Related
I'm trying to update a record with an admin user with a "validate" button that changes the record's status from pending to confirmed. I've created a form, and the route to do so, however the controller is giving me trouble, I'm not sure what to code for it to update the specific record i click validate for.
Hours controller
def index
#allhours = HourLog.where(status:'Pending')
end
def update
#hour = HourLog.where(status:'Pending')
#hour.update(#hour.id, :status)
end
Hours/index.html.erb
<td><%=form_for(:hour_log, method: :put) do |f|%>
<%= f.hidden_field :status, :value => 'Confirmed' %>
<%= f.submit 'Validate'%>
<%end%>
</td>
Any help would be fantastic, thanks!
error:
NoMethodError in HoursController#update
undefined method `id' for #
I know something is wrond with the (#hour.id) section of the controller in the update def, but I don't know what to replace it with
edit: rake routes
Prefix Verb URI Pattern Controller#Action
root GET / welcome#index
signup GET /signup(.:format) users#new
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
GET /users(.:format) users#index
PUT /users(.:format) users#update
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
dashboard GET /dashboard(.:format) hours#new
dashboard_hours GET /dashboard/hours(.:format) hours#index
hours PUT /hours(.:format) hours#update
GET /hours(.:format) hours#index
POST /hours(.:format) hours#create
new_hour GET /hours/new(.:format) hours#new
edit_hour GET /hours/:id/edit(.:format) hours#edit
hour GET /hours/:id(.:format) hours#show
PATCH /hours/:id(.:format) hours#update
PUT /hours/:id(.:format) hours#update
DELETE /hours/:id(.:format) hours#destroy
hours Controller
class HoursController < ApplicationController
before_action :require_user
before_action :require_admin, only: [:index]
def index
#allhours = HourLog.where(status:'pending')
end
def new
#hour = current_user.hour_logs.new
#entry = current_user.hour_logs.all
end
def edit
flash[:warning] = "Hash: #{params}"
end
def create
#hour = HourLog.new(hour_params)
#hour.user_id = current_user.id if current_user
#hour.status = 'pending'
if #hour.save
redirect_to '/dashboard'
end
end
private
def hour_params
params.require(:hour_log).permit(:assignment, :hours, :supervisor, :date)
end
def update_hour_params
params.require(:hour_log).permit(:status)
end
end
HourLog method
class HourLog < ActiveRecord::Base
belongs_to :user
end
I see this has an answer accepted, but I'd like to propose an alternative solution for anyone who may come along and find this question. Assumptions: The base page is an index with all pending items and the click is a simple state change. (A state toggle could be easily added to this solution.)
(I'm using the naming from the question.)
Using link_to as a message
A state change from 'Pending' to 'Confirmed' for a flag could be implemented by using the link_to to trigger the controller action to update this flag. Hash params may be passed via a link_to enabling simplified logic in the controller.
VIEW: Index.html.erb
<% #allhours.each do |hour| %>
<ul>
<li><%= hour.textdescriptionvar %> is an item pending approval.
<%= link_to(" Click to approve", edit_hour_path(hour, :status => "Confirmed"))
</li>
</ul>
<% end %>
CONTROLLER: HourLogsController
def edit
flash[:info] = "Hash: #{params}"
#hour = HourLog.find(params[:id])
end
def update
#hour = HourLog.find(params[:id])
#hour.update_attributes(hours_params)
if #hour.save
redirect_to #hour, :notice => "Successfully changed stat."
else
render 'index' # Any action here, index for example
end
end
private
def hours_params
params.require(:hour_log).permit(:status)
end
I would recommend a data: confirm message be added to the link_to,but I wanted to mimic the poster's situation as posted.
Rails spec for link_to here.
#hour = HourLog.where(status:'Pending') will return an ActiveRecord Relation, not a single record.
Try this:
#hour = HourLog.find_by(status: 'pending')
#hour.update_attribute :status, params[:hour_log][:status]
This method finds the first record with status ="pending" and updates that instead of the specific record.
You're going to want #hour.update params[:hour_log] instead because update_attribute doesn't run validations. and keep data in your database lowercase unless it's something written by a user. this link is useful http://www.davidverhasselt.com/set-attributes-in-activerecord/
So I am pretty stumped. I am new to Ruby on Rails (I am using Rails 4) and I have been trying to figure out for the last two days why my link_to tag keeps routing my login action to show instead. I removed the show action from my controller and even deleted show.html.erb and yet Rails remains persistent in trying to route it to a show action that no longer exists.
I removed all my redirect_to functions, and the link_to I create takes me to the correct page localhost:8000/users/login but now displays the error Unknown Action: The action 'show' could not be found for UsersController.
I have read up other SO questions that were similar, and some suggest that it may be an issue with jquery_ujs, which I removed from my file to see if it was the problem, but I still ended up with the same result.
The files in my views directory are as follows:
views
users
new.html.erb
login.html.erb
Here's what my code looks like:
The link_to in users/new (new.html.erb)
<li><%= link_to "Login", users_login_path %></li>
routes.rb
resources :users
root 'users#new'
get 'users/create'
get 'users/login'
users_controller.rb
class UsersController < ApplicationController
def new
end
def create
#user = User.create(:username => params[:username], :password => params[:password])
#user.save
#users = User.all
end
def login
#message = "Success"
end
end #end class
login.html.erb (Just testing an output here to see if it ever gets to this page)
<h3><%= #message %></h3>
Output of rake routes command
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
root GET / users#new
users_create GET /users/create(.:format) users#create
users_login GET /users/login(.:format) users#login
I figured out what the problem was:
I needed to remove resources :users from the routes.rb file.
Everything is working as expected now. After doing a little bit of research it seems that the problem with having resources :users in there is that when browsers try to access the page they try to perform a command by using an HTTP method, which is either GET, POST, PUT, DELETE, or PATCH.
When the page looks for an incoming command which in this case was GET /users/login, it tries to map it to a controller action. If the first matching route is resources :users, it will send it to the show action.
It seems that this is due to the default CRUD system Rails uses where each HTTP method represents a CRUD action (correct me if I am wrong):
GET is show
POST is create
DELETE is destroy
PATCH is update
I got most of this research from Rails Routing from the Outside In, Section 2.1.
I'm trying to figure out how to specify the route I'm trying to use but I keep getting routing errors. Can someone please point out where I'm going wrong and maybe explain what a better way to do this might be?
I'm working with these (nested) resources:
resources :users do
resources :playlists
end
Models:
class User < ActiveRecord::Base
has_many :playlists
end
class Playlist < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
Now I'm trying to link to a user's playlists inside the user/show.html.erb file:
<p>
<%= link_to "Playlists", user_playlists_path(#playlist)%></p>
</p>
Which brings me to a Playlists page (/users/1/playlists) successfully but when I try adding a new playlist for this user, I get the following error:
Showing /app/views/playlists/_form.html.erb where line #1 raised:
undefined method `playlists_path' for #<#<Class:0x0000000335c688>:0x00000003d0b238>
This is line #1:
<%= form_for(#playlist) do |f| %>
This is what I get for rake routes if that helps at all:
Prefix Verb URI Pattern Controller#Action
user_playlists GET /users/:user_id/playlists(.:format) playlists#index
POST /users/:user_id/playlists(.:format) playlists#create
new_user_playlist GET /users/:user_id/playlists/new(.:format) playlists#new
edit_user_playlist GET /users/:user_id/playlists/:id/edit(.:format) playlists#edit
user_playlist GET /users/:user_id/playlists/:id(.:format) playlists#show
PATCH /users/:user_id/playlists/:id(.:format) playlists#update
PUT /users/:user_id/playlists/:id(.:format) playlists#update
DELETE /users/:user_id/playlists/:id(.:format) playlists#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
root GET / default_pages#home
signup GET /signup(.:format) users#new
signin GET /signin(.:format) users#signin
The errors:
undefined method `playlists_path' for #<#<Class:0x0000000335c688>
and
user_playlist GET /users/:user_id/playlists/:id(.:format)
Gives a clear reason of the error you have.
<%= form_for(#playlist) do|f| %>
should be
<%= form_for([#user, #playlist]) do|f| %>
Or current_user, what is important is you need to pass the user object.
Explanation :
If you have noticed in a update action (taking that you redirect to a show page after you update) instead of redirect_to user_path(#user) we can do just redirect_to #user, from which Rails infers that you are redirecting to the show path of user.
It is a similar situation here, similarly if you are having a form_for for the playlist and you pass only #playlist instead of [#user, #playlist] then it woud try to find new_playlist_path, which isn't in your route and will show an error.
This is a short gist on how you might do it.
This:
<%= form_for(#playlist) do |f| %>
Should be This:
<%= form_for [#user, #playlist] do |f| %>
note the square brackets
Something weird is happening and I don't know why.
When I use the helper <%= link_to "New game", new_game_path %>, my new game form does not submit.
But when I acesses the view typing the URL localhost:3000/games/new form works just well
Any idea how to solve that?
Thanks,
Here my rake routes
Prefix Verb URI Pattern Controller#Action
root GET / games#index
user_sessions GET /user_sessions(.:format) user_sessions#index
POST /user_sessions(.:format) user_sessions#create
new_user_session GET /user_sessions/new(.:format) user_sessions#new
edit_user_session GET /user_sessions/:id/edit(.:format) user_sessions#edit
user_session GET /user_sessions/:id(.:format) user_sessions#show
PATCH /user_sessions/:id(.:format) user_sessions#update
PUT /user_sessions/:id(.:format) user_sessions#update
DELETE /user_sessions/:id(.:format) user_sessions#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
delete_progress_progresses POST /progresses/delete_progress(.:format) progresses#delete_progress
progresses POST /progresses(.:format) progresses#create
search GET /search(.:format) games#search
game_levels GET /games/:game_id/levels(.:format) levels#index
POST /games/:game_id/levels(.:format) levels#create
new_game_level GET /games/:game_id/levels/new(.:format) levels#new
edit_game_level GET /games/:game_id/levels/:id/edit(.:format) levels#edit
game_level GET /games/:game_id/levels/:id(.:format) levels#show
PATCH /games/:game_id/levels/:id(.:format) levels#update
PUT /games/:game_id/levels/:id(.:format) levels#update
DELETE /games/:game_id/levels/:id(.:format) levels#destroy
insert_levels_game POST /games/:id/insert_levels(.:format) games#insert_levels
games GET /games(.:format) games#index
POST /games(.:format) games#create
new_game GET /games/new(.:format) games#new
edit_game GET /games/:id/edit(.:format) games#edit
game GET /games/:id(.:format) games#show
PATCH /games/:id(.:format) games#update
PUT /games/:id(.:format) games#update
DELETE /games/:id(.:format) games#destroy
login GET /login(.:format) user_sessions#new
logout POST /logout(.:format) user_sessions#destroy
My route file
Rails.application.routes.draw do
root :to => 'games#index'
resources :user_sessions
resources :users
resources :progresses, :only => :create do
collection do
post 'delete_progress'
end
end
get 'search' => 'games#search'
resources :games do
resources :levels
member do
post 'insert_levels'
end
end
get 'login' => 'user_sessions#new', :as => :login
post 'logout' => 'user_sessions#destroy', :as => :logout
end
Sure, you can use string as relative URL. This will never crash since rails will not try to resolve your routes building it. My guess is, that you might have a typo of some sort.
There is no reason why this wouldn't work. I have searched your git app for "new_game_path" but could not find a single example where you use this code.
I have only found < a href="/games/new">New game</a> in your layout.
Replace it with <%= link_to 'New Game', new_game_path %> this works in your app. I have just tested it.
If you intend to use internationalization at some point, you should avoid standard HTML links. They will not keep your locale persistent.
form
You mention that your "form does not submit"
This is not a problem with your link_to - it's an issue with your form; they are two different issues:
--
link_to
link_to takes you to a new page. It's a helper method to help create the equivalent of Your text
This means that if you're sending requests to your new action, it should not matter how the users get there - only how the action is rendered.
The typical case for a form is as follows:
#app/views/games/new.html.erb
<%= form_for #game do |f| %>
<%= f.text_field :attribute %>
<%= f.submit "test" %>
<% end %>
#app/controllers/games_controller.rb
Class GamesController < ApplicationController
def new
#game = Game.new
end
end
--
Fix
When you mention your new game form does not submit, that's an issue with your form itself. This could be due to a number of reasons, but typically with the way in which you're rendering the form
To fix this, you'll need to detail how you're rendering your form & how you'd like it to submit
Update
Having read your updated comments, if the form works when you send the requests to the "naked" url, the issue may not be with the form itself.
As a rule of thumb, you'll always want to use the Rails helpers when defining links etc. In your application layout, I found that you posted "pure" HTML to create a link. This is bad because if the Rails syntax changes, or your routes change, your application won't update correctly.
I've over written the registration controller and I'm trying to return to the form to create the user, but it only works partly.
def create
u = User.find_by_email(params[:user][:email])
...
if u && u.unregistered
self.resource = u
if resource.update_with_password(params[resource_name])
...
set_flash_message :notice, :updated if is_navigational_format?
...
sign_in(resource_name, resource)
if u.confirmed?
redirect_to consultations_path
else
redirect_to send_advisor_confirmation_path(u.id)
end
else
clean_up_passwords(resource)
# this is the place that has a problem
respond_with_navigational(resource) { render_with_scope :new }
end
return
end
super
end
(side note: in my app it is possible to create a user who hasn't signed in yet, they are defined as being "unregistered", and they can claim their account by going through the sign up process).
This respond_with_navigational works the first time (when it is new posting to create) but then when you mess the form up again it clears the whole form (when create is posting to create... or should be). The logs say that the first time it is going to create, but the second time it is going to update:
Started POST "/users" for 127.0.0.1 at 2011-07-20 15:49:30 -0500
Processing by RegistrationsController#create as HTML
...
Started POST "/users" for 127.0.0.1 at 2011-07-20 15:50:56 -0500
Processing by RegistrationsController#update as HTML
According to the routes (rake routes):
POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
So why would the send post be directed to the update? And how can I fix this?
Update: Ok, so it is being redirected to update (I believe) because resource is being defined as an existing object. But because I have my custom strategy for devise saying that this particular user isn't authenticated, then it is unable to go to update (which requires authentication) and is redirected to new, but without parameters.
So the question becomes, how do I setup resource so it looks like a new user, but has the errors that it has before creating the new user.
For example an error as is might be "Passwords not matching", but if you find the errors on a new user it would be "Email already taken".
EDIT 1:
I missed some of your question, this may not be helpful.
Make sure you're using something like this to display it on your page:
layouts/application.html.erb (or wherever you need it)
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
I don't like this, but it works. If anyone has a better way of doing this, please let me know. Thank you.
...
else
resource.unregistered_advisor = true
clean_up_passwords(resource)
a = User.new(:email => resource.email, :name => resource.name, :confirmation_token => resource.confirmation_token)
resource.errors.each do |key,value|
a.errors[key] = value
end
self.resource = a
respond_with_navigational(resource) { render_with_scope :new }
end
...