I've read a few similar questions/answers but cannot find any answers to my bug.
I have a form for entering hours for a job and one for editing an hours record. Both the add and edit share a partial as per Rails guidelines. My add (new) form works fine. When submitting the edit form, I get the error/bug:
Unknown Action
The action '11' could not be found for HoursController
I get no other output on this screen.
The url shown for these screens are:
http://localhost:3000/hours/11/edit
after submit:
http://localhost:3000/hours/11
The form tag for my edit form is:
<%= form_for(:hour, :url => {:action => 'update', :id => #hour.id}) do |f| %>
This should go to the 'hours' controller, to the 'update' action. Maybe there is something else wrong with my controller, but the add/new form works fine... confusing...
My Controller:
class HoursController < ApplicationController
before_filter :confirm_logged_in
def index
end
def list
if !params[:job_id].nil?
#hours = Hour.where(["job_id = ?",params[:job_id]]).date_sorted
else
#hours = Hour.date_sorted
end
end
def show
#hour = Hour.find(params[:id])
end
def new
#hour = Hour.new
end
def create
#hour = Hour.new(params[:hours])
if #hour.save
job = Job.find(params[:hours][:job_id])
flash[:notice] = "You have just entered hours for #{job.name}"
redirect_to(:action => "list", :job_id => params[:hours][:job_id])
else
flash[:notice] = "There were one or more problems with your form. Please try again!"
render('new')
end
end
def edit
#hour = Hour.find(params[:id])
end
def update
#hour=Hour.find(params[:id])
if #hour.update_attributes(params[:hour])
flash[:notice] = "You have just updated an Hours entry"
redirect_to(:action => "list", :job_id => params[:hours][:job_id])
else
render("edit")
end
end
def delete
#hour = Hour.find(id)
end
def destroy
Hour.find(params[:id]).destroy
flash[:notice] = "Hours Deleted Successfully!"
redirect_to(:action => 'list')
end
def search
if params[:job][:id]
#job = Job.find(params[:job][:id])
redirect_to(:action => "list", :job_id => #job.id)
end
end
end
My Routes file for the Hours section
resources :hours do
collection do
get :list
get :delete
end
end
Which gives me:
list_hours GET /hours/list(.:format) hours#list
delete_hours GET /hours/delete(.:format) hours#delete
hours 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
PUT /hours/:id(.:format) hours#update
DELETE /hours/:id(.:format) hours#destroy
Any ideas in how to figure this out would be greatly appreciated.
--jc
Try like this
<% form_for #hours do |f| %>
or else
<% form_for :hours, :url =>
url_for(:action => "update", :id => #hours) do |f| %>
My situation was slightly different because I had a custom route, but it may help someone else (or future me).
I kept getting that error until I added the method: :post to the form params.
routes:
resources :hours do
member do
post :fix
end
end
html.erb
form_for #hour, url: fix_hour_path(#hour), method: :post do
...
end
Related
I started learning Rails for about a month now and I am working on a fairly simple project to improve my skills. It's a blog where editors can add articles and users can subscribe to a newsletter by adding their email. The homepage is an index view and their is a footer that shows up across all pages.
Here is my problem: I would like to include the form_for the newsletter on the footer that exists inside the application layout. That form has a specific model: Subscriber, which stores users emails.
What I've done so far is include the following on the Articles controller:
def index
#articles = Article.order(created_at: :desc).all
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.new(subscriber_params)
if #subscriber.save
redirect_to '/home'
else
render 'new'
end
end
def subscriber_params
params.require(:subscriber).permit(:email)
end
In application.html.erb :
<%= form_for(#subscriber) do |f| %>
<%= f.email_field :email, :placeholder => "email address" %>
<%= f.submit 'Sign up', :id => "signup" %>
<% end %>
The form is displayed correctly on the index page only and it doesn't save to the database (without error).
I have tried using a before filter on the application controller as well as rendering a partial without any success.
Edit
Subscribers controller code:
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.new(subscriber_params)
if #subscriber.save
redirect_to '/home'
else
render 'new'
end
end
private
def subscriber_params
params.require(:subscriber).permit(:email)
end
Application controller:
before_action :create_new_subscriber
def create_new_subscriber
#subscriber = Subscriber.new
end
I'm still (for those that have helped before) having issues validating data.
The Scenario
I have two models - user and accommodation with each user having one accommodation (has_one). I am able to access the logged in user using current_user.
The Problem
When I validate users upon registration everything works fine with validation error messages displayed accordingly for each validation rule. However, I am now trying to validate accommodations when they are entered and I get a Rails error page:
Unknown Action
The action 'show' could not be found for AccommodationsController
but interestingly the url has changed to /accommodations//edit which appears to be missing an id for the accommodations id (I do want it to divert to edit if everything is ok).
I don't think it's the validation rules themselves but more how I am handling redirection (which is confusing me to be honest!). The data saves correctly and redirects correctly if it passes the validation rules but not sure how to handle a "non-save" gracefully.
The Code
Accommodation Model
class Accommodation < ActiveRecord::Base
belongs_to :user
#validation rules
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
end
/accommodations/new.html.erb
...
<%= form_for :accommodation, url: accommodations_path do |f| %>
<% if #accommodation.errors.any? %>
<% #accommodation.errors.full_messages.each do |msg| %>
<p class="error"><%= msg %></p>
<% end %>
<% end %>
...
AccommodationsController (thanks to #depa for help with this)
...
def index
if current_user.accommodation.present?
redirect_to edit_accommodation_path(current_user.accommodation)
else
redirect_to new_accommodation_path(current_user.accommodation)
end
end
def new
#accommodation = Accommodation.new
end
def create
current_user.create_accommodation(accommodation_params)
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
end
def edit
end
def update
if #accommodation.update(accommodation_params)
flash[:success] = "Accommodation details updated successfully"
redirect_to edit_accommodation_path(#accommodation)
else
flash[:error] = "Accommodation details could not be updated"
render 'edit'
end
end
private
def accommodation_params
params.require(:accommodation).permit(:name, :email)
end
...
To handle your failed validations gracefully:
def create
#accommodation = current_user.build_accommodation(accommodation_params)
if #accommodation.save
flash[:success] = "Accommodation details added"
redirect_to edit_accommodation_path(current_user.accommodation)
else
flash.now.notice = "Error creating accommodation"
render "new"
end
end
Is accommodation_params actually set up on entry to create? I'd expect you to need to use params or params[:accommodation].
If it isn't, the create_accommodation call is going to fail, which will mean current_user.accommodation will be nil, which may well produce your error.
well this problem I'm sure it isn't difficult at all but being new to rails I'm kinda lost.
After watching Railscasts Episode #52, I went on to make my own publish/unpublish list of slides.
So this is the form view
= form_tag publish_admin_category_slides_path(#cat4), :method => "put" do
#pricing
%table#plans
%thead
%tr
%th Image
%th Published
%tbody
- #image.each do |im|
%tr.odd
%td
= image_tag im.avatar.url(:thumb)
= link_to "Delete", admin_category_slide_path(#cat4,im), :method => "delete"
%span is
%th
= check_box_tag "slide_published[]", im.published ,im.published
With controller action #publish
def publish
Slide.update_all(:published => params[:slide_published])
redirect_to :action => "index"
end
What would be the correct way to write something like this?
A list with checkboxes that changes boolean state of an attribute and multi updates?
Some xp l8r I was able to find something like this.
I post it in case someone has a similar problem
def publish
#cat = Category.find(params[:category_id])
if params[:slide_published]
checked_slides = Slide.find(params[:slide_published])
end
unless checked_slides.blank?
#cat.slides.all.each do |slide|
if checked_slides.include?(slide)
slide.update_attribute :published, true unless slide.published?
else
slide.update_attribute :published, false unless !slide.published?
end
end
end
if checked_slides.blank?
#cat.slides.all.each do |slide|
slide.update_attribute :published, false unless !slide.published?
end
end
I have a form in an account_settings controller that updates a user object through the users_controller. The error messages are not getting passed through. Here's a look at the code.
account_settings/account.html.haml
= form_for #user, :url => { :controller => "users", :action => "update", :id => #user.id } do |f|
- if #user.errors.any?
.alert.alert-error
%h4
Please address the following errors:
%ul
- #user.errors.full_messages.each do |msg|
%li= msg
form stuff...
account_settings_controller
def account
#user = current_user
end
users_controller
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
end
redirect_to :back
end
The form will not update but there are no error messages getting passed back. Any thoughts?
This happens because you are returning a redirecting. The #user object does not persist across the redirect. You should be doing something like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:notice] = "User was successfully updated."
redirect_to :back
else
render :edit
end
end
Here, we redirect when the update is successful, but if not, we render the edit action. Hence we have access to #user, and your errors would be present in #user.errors
I have two models, Post and Comment that have a polymorphic association with another model called Vote.
post.rb and comment.rb have has_many :votes, :as => :votable, :dependent => :destroy
vote.rb has belongs_to :votable, :polymorphic => true
This controller has two actions one to add up votes for Post and the other for Comment:
controllers/votes_controller.rb:
class VotesController < ApplicationController
def vote_up
#post = Post.find(params[:id])
if #post.votes.exists?(:user_id => current_user.id)
#notice = 'You already voted'
else
#vote = #post.votes.create(:user_id => current_user.id, :polarity => 1)
end
respond_to do |format|
format.js
end
end
def vote_up2
#comment = Comment.find(params[:id])
if #comment.votes.exists?(:user_id => current_user.id)
#notice2 = 'You already voted'
else
#vote2 = #comment.votes.create(:user_id => current_user.id, :polarity => 1)
end
respond_to do |format|
format.js
end
end
end
I think that's unnecesary. Is there any way of using a single name to refer to the current votable element or either #post and #comment?
Edit
routes.rb:
get 'votes/:id/vote_up' => 'votes#vote_up', as: 'vote_up'
get 'votes/:id/vote_down' => 'votes#vote_down', as: 'vote_down'
The vote_up action should be implemented in your posts and comments controller respectively. Users are voting on posts or comments, they're not voting on a vote.
I would extract the voting logic and place it in a module that your models will include, then call it on a votable object from the controller.
in your lib directory, create votable.rb
module Votable
def up_vote_from(usr)
place_vote(1, usr.id)
end
def down_vote_from(usr)
place_vote(-1, usr.id)
end
private
def place_vote(direction, usr_id)
v = self.votes.find_or_create_by_user_id(usr_id)
v.update_attribute(:polarity, direction)
end
end
(This revised code will alter a user's original vote if they vote again. Vote methods will return true if the vote saves, false otherwise.)
In each votable model, such as post.rb and comment.rb, add this line to mix in your voting methods:
include Votable
Now, this can be done in a controller:
#post.up_vote_from current_user # => true
As far as implementation is concerned, you will end up with some repetition in your controllers/routes.
In each votable controller, set something up like:
def cast_vote
#post = Post.find params[:id]
if #post.call("#{params[:updown]}_vote_from", current_user)
respond_to do |format|
format.js
end
else
head :not_found
end
end
(this expects .../posts/123/vote/up for an upvote, .../posts/123/vote/down for a downvote.)
then append each resource to include your vote method:
resources :posts do
member do
post 'vote/:updown', :to => "posts#cast_vote", :as => :vote_on
end
end
which can be called in your views with:
<%= button_to "Up", :url => vote_on_post_path(#post, "up"), :remote => true %>
<%= button_to "Down", :url => vote_on_post_path(#post, "down"), :remote => true %>
This is a lot less work than it looks. It'll make sense once you put it in place. It'll make even more sense if you code it in by hand vs. cut and paste. :)